demo是封裝了一個控件,直接在MainViewController
的viewWillAppear里初始化,并且調用一個初始化滾動到中間的方法,方法主要是調用了
-(void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated;
方法,在初始化后將其滾動到中間的區域。不過,當我在項目里使用的時候,遇到了調用該方法卻無法滾動到中間的情況。我的使用步驟是:在我界面的UIView
中,創建該控件對象并且調用滾動到中間的方法。
scrollToItemAtIndexPath使用
發現效果無法實現,我第一時間檢查了函數有沒有調用,然后發現是調用的了,但沒有出現該出現的效果。所以簡單看一下該方法的官方注釋。xcode提示的描述是:Scrolls the collection view contents until the specified item is visible.
只是方法的作用,并沒有說使用條件。為了快速解決問題,google了一下scrolltoitematindexpath not working
,在這里:link找到了答案:
不知道這是一個bug還是一個特性,每當在UICollectionView
顯示它的subview
之前調用scrollToItemAtIndexPath:atScrollPosition:Animated
方法,UIKit就會報錯。所以要解決它,就應該在viewController
中,在你能確認CollectionView
完全計算出其subview
布局的地方去調用這個方法。比如在viewDidLayoutSubviews
里調用就沒有問題。
方法的意思已經明確,就是找到一個能計算出collectionview
的所有布局地方調用滾動方法。但我的界面使用的是自動布局,只在模塊外有一個viewController
,其余的都是在UIView
中創建添加,所以在我使用這個控件對象的地方,我無法復寫viewController
的方法。所以想了幾個辦法。
-layoutsubviews
既然情況發生在UIView
中,那首先想到的是重寫該方法。重寫之前,看一下文檔說明:
Lays out subviews.
DiscussionThe default implementation of this method does nothing on iOS 5.1 and earlier. Otherwise, the default implementation uses any constraints you have set to determine the size and position of any subviews.
Subclasses can override this method as needed to perform more precise layout of their subviews. You should override this method only if the autoresizing and constraint-based behaviors of the subviews do not offer the behavior you want. You can use your implementation to set the frame rectangles of your subviews directly.
You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
該方法為子view布局。子類可以復寫該方法為子view實現更精確的布局。但你只應該在用自動布局時無法實現效果時用它。而且你不應該直接調用該方法,如果需要強制刷新布局,調用setNeedLayout
,會在下一次繪制刷新前更新布局。調用layoutIfNeed
可以立即刷新布局。
看起來這個方法有用,我便試了一下。
[self setNeedLayout];
[self layoutIfNeed];
[myPicker scrollToCenter];
然而還是沒有效果。
[view.window]
前面的方法沒效果,我又想了一下scrolltoitematindexpath
的實現條件,是在UICollectionView
顯示之后調用才有效,所以我需要在UIView中獲得它顯示的狀態再去滾動。幸好,我的控件也是繼承UIView的,其中有這么一個屬性:
Property: windowThis property is nil if the view has not yet been added to a window.
也就是說這個屬性值代表著這個view
有沒有放在窗口中顯示,那么我就需要在當前UIView
的生命周期中檢查myPicker
對象的這個屬性就好了。所以最后的解決方法是,起一個子線程,對myPicker
的狀態進行檢查,當狀態為顯示的時候調用滾動方法:
NSThread *checkShownThread;
checkShownThread = [[NSThread alloc] initWithTarget:self selector:@selector(checkIfViewIsShowing) object:nil];
[checkShownThread start];
- (void)checkIfViewIsShowing {
while (1) {
if (hPicker.window != nil) {
break;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[hPicker scrollToCenter];
});
[checkShownThread cancel];
checkShownThread = nil;
但不建議開線程(費性能),所以可以外接屬性,在控制器- (void)viewDidLayoutSubviews中去寫該方法
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self.rewardHeaderView.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:100/2] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
}