最近大半個月的時間一直忙于編寫一個不大但是也不算簡單的小項目, 更新博客的頻率低了好多. 今天發(fā)現(xiàn)并解決了一個問題, 由于查找解決問題的途中發(fā)現(xiàn)很少人遇到這個問題, 覺得拿出來說說, 說不定會給以后遇到的人帶來一些便利.
首先, 頁面的需求是這樣的:
1.頁面上半部分是可以橫向分頁滾動的視圖A, 每頁顯示一個圖表
2.隨著視圖A滾動到不同的頁數(shù), 下半部分(視圖B)變換不同的數(shù)據(jù)
3.視圖A每頁的圖表有多處可以點擊,并且點擊事件也會觸發(fā)視圖B數(shù)據(jù)顯示的變換.
用圖來表示一下:
整體的思路并不難, 我采取了下面的方案:
1.頂部使用UICollectionView作為滾動視圖, 使其cell大小等于自身大小, 每個cell展示一個圖表
2.每次滾動完畢及點擊cell中圖表的點時, 獲取collectionView當(dāng)前展示的cell的indexPath,以此獲取該坐標(biāo)對應(yīng)的值展示到下方.
所以,這個功能的關(guān)鍵點就在于如何獲取頂部CollectionView當(dāng)前顯示的cell的indexPath.
我先后試過以下幾種方案:
方案一:
通過之前做過的自動輪播的廣告banner的經(jīng)驗(和這個需求有相似的地方),我先采用了之前的方法:
通過UICollectionView的-collectionView:didEndDisplayingCell:forItemAtIndexPath:
方法得到indexPath更新的時刻, 然后用[collectionView indexPathsForVisibleItems]
獲取一組當(dāng)前顯示的cell們的indexPath的數(shù)組,由于我的cell大小剛好等于collectionView的大小,所以這個數(shù)組只包含一個元素(后來證明了即使是這樣,這個數(shù)組也不一定只有一個元素),我只要取里面的第一個就好了.
- (void)collectionView:(UICollectionView *)collectionView
didEndDisplayingCell:(UICollectionViewCell *)cell
forItemAtIndexPath:(NSIndexPath *)indexPath {
// 獲取當(dāng)前顯示的cell的下標(biāo)
NSIndexPath *firstIndexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
// 賦值給記錄當(dāng)前坐標(biāo)的變量
self.currentIndexPath = indexPathNow;
// 更新底部的數(shù)據(jù)
// ...
}
這個方案對于此頁面本身基本可以達(dá)到要求,但是由于下級頁面可能會修改到這個頁面的元素,導(dǎo)致cell個數(shù)增加或者減小, 有些情況下回到此頁面時會造成程序崩潰.
打個比方: 我的collectionView的cell共有3個,并且將cell滑動到最后一頁(最后一個cell),此時記錄當(dāng)前的下標(biāo)為2. 下級頁面刪除了一個元素,再回到此頁面的時候,didEndDisplay...方法會最先被調(diào)用,這個方法里面會取數(shù)據(jù)重新設(shè)置下半部分視圖的顯示, 而此時當(dāng)前的下標(biāo)仍為2, 數(shù)據(jù)只剩兩個了,就導(dǎo)致了應(yīng)用的崩潰.
如果從子頁面回來didEndDisplay...方法不被調(diào)用或者比較后面被調(diào)用,就不會出現(xiàn)這種情況,但是這個方法的調(diào)用時機豈是我等可以控制的? 于是乎換了一個方案.
方案二:
通過UIScrollView的-scrollViewDidEndDecelerating:
方法得到indexPath更新的時刻, 然后用[collectionView indexPathsForVisibleItems]
獲取一組當(dāng)前顯示的cell們的indexPath的數(shù)組,和上面的方案一樣,我只要取里面的第一個.
didEndDecelerating是在scrollView(collectionView)減速完成(滾動停止)的時刻調(diào)用的,由于又是pageEnabled == YES, 這個時刻只有一個cell在屏幕上.而且didEndDecelerating不會在從子頁面返回的時候調(diào)用,也就解決了方案一的問題.
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// 獲取當(dāng)前顯示的cell的下標(biāo)
NSIndexPath *firstIndexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
// 賦值給記錄當(dāng)前坐標(biāo)的變量
self.currentIndexPath = indexPathNow;
// 更新底部的數(shù)據(jù)
// ...
}
我曾以為問題就這樣解決了,結(jié)果意外地發(fā)現(xiàn), 有些時候我點擊cell中圖表的某個點時,cell會跳變?yōu)楦舯诘腸ell,查了良久,才發(fā)現(xiàn)就是由于對collectionView當(dāng)前的坐標(biāo)獲取不準(zhǔn)確導(dǎo)致的, 進一步地發(fā)現(xiàn),這個不準(zhǔn)確就是由于:即使我的cell大小等于collectionView的大小,在[collectionView indexPathsForVisibleItems]
方法得到的數(shù)組數(shù)據(jù)也有可能大于一個.并且順序還不一定,導(dǎo)致我也不能通過firstObject或者lastObject來獲取我想要的值(我也不知道為什么會大于一個還有不確定的順序).
方案三:
再后來,我找到了這個方法:indexPathForItemAtPoint:
---根據(jù)某一點得到位于這點上的cell的坐標(biāo),而且返回值是一個確定的indexPath,而不是數(shù)組,正合我意.需要注意的是,這個方法傳入的參數(shù)point是以scrollView的contentView為坐標(biāo)系的坐標(biāo),使用之前要先進行一次轉(zhuǎn)換.最終我的做法是這樣:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// 將collectionView在控制器view的中心點轉(zhuǎn)化成collectionView上的坐標(biāo)
CGPoint pInView = [self.view convertPoint:self.collectionView.center toView:self.collectionView];
// 獲取這一點的indexPath
NSIndexPath *indexPathNow = [self.collectionView indexPathForItemAtPoint:pInView];
// 賦值給記錄當(dāng)前坐標(biāo)的變量
self.currentIndexPath = indexPathNow;
// 更新底部的數(shù)據(jù)
// ...
}
問題解決了.