BSLoopView 實(shí)現(xiàn)了常用的 banner 無(wú)限循環(huán)輪播圖呻澜,支持 2D滥崩、3D 樣式
效果圖
主要知識(shí)點(diǎn):
- 無(wú)限輪播原理
- 自動(dòng)輪播 timer 導(dǎo)致頁(yè)面無(wú)法釋放問(wèn)題
- collectionView 自定義分頁(yè)大小及UICollectionViewFlowLayout其他問(wèn)題
1据忘、無(wú)限輪播原理
輪播原始數(shù)據(jù)為 NSArray *dataArr = @[1,2,3]
鹦牛,構(gòu)造新數(shù)據(jù),添加三次數(shù)據(jù)源
[self.newDataArr removeAllObjects];
[self.newDataArr addObjectsFromArray:dataArr];
[self.newDataArr addObjectsFromArray:dataArr];
[self.newDataArr addObjectsFromArray:dataArr];
新數(shù)據(jù)為 self.NewDataArr = @[1,2,3,1,2,3,1,2,3]
新數(shù)據(jù)構(gòu)造完成后若河,將collectionView的初始位置更改為中間的數(shù)據(jù)源起始位置,也就是第二個(gè)1的位置(下標(biāo)為3)
self.currentPageIndex = dataArr.count;
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath
indexPathForRow:self.currentPageIndex inSection:0]
atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
這樣就確保了scrollVeiw的 兩邊都是有數(shù)據(jù)的能岩,我們要做的就是利用setContentOffset
方法,讓 collectionView 始終展示中間的數(shù)據(jù)源即可
示例代碼
NSInteger newPageIndex = self.currentPageIndex;
if (self.currentPageIndex < self.dataArr.count) {
newPageIndex = self.dataArr.count + self.currentPageIndex;
}else if (self.currentPageIndex >= self.dataArr.count*2){
newPageIndex = self.currentPageIndex - self.dataArr.count;
}
計(jì)算了當(dāng)前頁(yè)面的pageIndex后萧福,通過(guò) pageIndex 計(jì)算實(shí)際偏移量拉鹃,然后 setContentOffset
※※※
為什么這里要用 setContentOffset ,而不用 scrollToItemAtIndexPath 呢鲫忍,因?yàn)槲以谑褂?scrollToItemAtIndexPath 發(fā)現(xiàn)如果 使用3d效果膏燕,就會(huì)出現(xiàn)UI錯(cuò)亂的問(wèn)題,使用3D效果+ scrollToItemAtIndexPath悟民,打印出來(lái)的偏移量發(fā)現(xiàn)并不正確坝辫,具體原因沒(méi)找到
2、自動(dòng)輪播 timer 導(dǎo)致頁(yè)面無(wú)法釋放問(wèn)題
如果 調(diào)用 scheduledTimerWithTimeInterval 方法執(zhí)行timer時(shí) 射亏,repeats:YES的話近忙,timer就會(huì)強(qiáng)持self,導(dǎo)致self無(wú)法釋放智润。一般情況下我們只要在需要的時(shí)候及舍,調(diào)用
[self.timer invalidate];
self.timer = nil;
就可以停止timer,然后釋放 self
但是很多時(shí)候窟绷,我們無(wú)法在確定的某個(gè)點(diǎn)去做 timer 的置空锯玛,所以我們需要使用其他方法,讓 timer 不在強(qiáng)持 self 兼蜈。
使用NSObject 類(lèi)提供的方法攘残,實(shí)現(xiàn)消息轉(zhuǎn)發(fā)可以解決此問(wèn)題
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
- (void)forwardInvocation:(NSInvocation *)invocation
具體做法:自定義一個(gè) 中間類(lèi),繼承NSObject为狸,然后引入 weak 聲明需要發(fā)送消息的對(duì)象
中間類(lèi) TimerTarget 如下:
@interface TimerTarget : NSObject
@property (nonatomic ,weak) BSLooperView * target;
@end
#pragma mark -
@implementation TimerTarget
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
@end
我們?cè)诔跏蓟痶imer的時(shí)候 歼郭,將self 作為 TimerTarget 的target,然后將TimerTarget 作為timer的target 即可
self.timerTarget = [[TimerTarget alloc]init];
self.timerTarget.target = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.duration
target:self.timerTarget selector:@selector(looperTime) userInfo:nil
repeats:YES];
3辐棒、collectionView 自定義分頁(yè)大小及UICollectionViewFlowLayout其他問(wèn)題
自定義分頁(yè)
自定義 UICollectionViewFlowLayout病曾,重寫(xiě) prepareLayout 方法 姊途,利用消息轉(zhuǎn)發(fā),重新設(shè)置頁(yè)面間距知态,頁(yè)面坐標(biāo)
/// collectionView 的 寬度 去掉 item 寬度 CGFloat contentInset = self.collectionView.width - self.itemSize.width; /// 減速模式 self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast; /// 算出內(nèi)邊距 self.collectionView.contentInset = UIEdgeInsetsMake(0, contentInset*0.5, 0, contentInset*0.5); /// 設(shè)置每個(gè)頁(yè)面之間的間距 if ([self.collectionView respondsToSelector:NSSelectorFromString(@"_setInterpageSpacing:")]) { ((void(*)(id,SEL,CGSize))objc_msgSend)(self.collectionView,NSSelectorFromString(@"_setInterpageSpacing:"),CGSizeMake(-(contentInset-self.minimumLineSpacing), 0)); } /// 設(shè)置 page 的坐標(biāo)(原理:正常坐標(biāo)是 0.0,如果想讓左邊留下 30 的寬度立叛,就需要 page 左移 30) /// 左移如果想要 item 的邊距均分负敏,就需要 左移 contentInset*0.5 if ([self.collectionView respondsToSelector:NSSelectorFromString(@"_setPagingOrigin:")]) { ((void(*)(id,SEL,CGPoint))objc_msgSend)(self.collectionView,NSSelectorFromString(@"_setPagingOrigin:"),CGPointMake(-contentInset*0.5, 0)); }
- layoutAttributesForElementsInRect 方法
我們?cè)谥貙?xiě) layoutAttributesForElementsInRect 時(shí)NSArray *originArr = [super layoutAttributesForElementsInRect:rect];
不能直接修改originArr數(shù)組內(nèi)容,需要將他copy后再返回
NSArray *originArr = [super layoutAttributesForElementsInRect:rect]; NSArray * array = [[NSArray alloc]initWithArray:originArr copyItems:YES]; for (UICollectionViewLayoutAttributes * attrs in array) { /// 修改 }
如果你的布局需要?jiǎng)討B(tài)更新秘蛇,則需要調(diào)用此方法
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES; }