iOS 自定義圖片選擇器 3 - 相冊列表的實現(xiàn)(UICollectionView)

寫在前面
筆者按照Instagram的圖片選取器寫了個小Demo慨仿,
該系列文章是以實現(xiàn)Demo目的來逐個介紹使用到的東西灶体,若有不正確的地方還望指出來其障,共同學(xué)習(xí)。
地址:https://github.com/BigBigPo/RJPhotoPicker


UICollectionView鳄逾,自從推出一來就受到廣大的iOS開發(fā)者對其贊不絕口稻轨,其高度的靈活性使得其自身的可定制化程度極高。開發(fā)者們用其做出了諸多絢麗的效果雕凹。

這要歸功于UICollectionViewLayout殴俱,它CollectionView進行自定義布局的重要基石,只有掌握了它才能說自己掌握了CollectionView枚抵,Layout到底有多強大线欲,筆者會單獨新開一篇來介紹。本節(jié)主要還是以達到我們系列文章所要實現(xiàn)的Demo效果為目的汽摹,對CollectionView有一個簡單的介紹李丰。

好啦,我們先看一下我們要仿照的Instagram的圖片選擇器


Insshow.gif

上一節(jié)我們實現(xiàn)了上方“展示區(qū)”的效果逼泣,十分簡單趴泌,核心就是UIScrollView的縮放與圖片的布局更新舟舒。這一節(jié)我們要實現(xiàn)下方的列表。

下方的列表可以直接想到UICollectionView,它對于這種類型的列表再適合不過了嗜憔,實現(xiàn)起來非常簡單秃励,好在Instagram的列表部分并不復(fù)雜,我們不需要自己去自定義collectionViewLayout吉捶,使用系統(tǒng)提供的UICollectionViewFlowLayout就可以輕松實現(xiàn)這種流式布局的效果夺鲜。

至于UICollectionView與UITableView的關(guān)系······
UICollectionView完全可以實現(xiàn)UITableView的效果,之前看到有大牛發(fā)現(xiàn)了UIKit框架中更新了UICollectionViewTableLayout這樣的東西(似乎是這個名字呐舔,不知真假币励。)【iOS14中已添加,詳見Lists】滋早,兩者的關(guān)系就不言而喻了榄审。

1. UICollectionView的基本使用

UICollectionView的使用上與UITableVIew極其類似,兩者都是繼承自UIScrollView杆麸,具備ScrollView的所有特性,UICollectionView的初始化是這樣的

    _collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
    [_collectionView setDelegate:self];
    [_collectionView setDataSource:self];
    [_collectionView registerNib:[UINib nibWithNibName:@"RJPhotoCell" bundle:nil] forCellWithReuseIdentifier:RJPhotoPickerCellID];

初始化時附帶了layout浪感,布局昔头,決定了UICollectionView會以何種方式展示,如行間距影兽,item的大小揭斧,間隔大小等等,這些會在談layout的文章中再展開峻堰,我們的相冊選擇器直接用系統(tǒng)提供的流式布局UICollectionViewFlowLayout讹开,不用我們自己操心如何去寫布局的代碼。

    UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
    layout.itemSize = cellSize;
    layout.minimumInteritemSpacing = 1;
    layout.minimumLineSpacing = 1;

我們這里只設(shè)置了間距與大小捐名,這些就足夠了旦万,當然,這些配置也
可以留到layout的代理里面設(shè)置镶蹋,但我們的選擇器并沒有復(fù)雜的布局成艘,專門寫在代理里有點奇怪。

順帶提一下贺归,UICollectionViewFlowLayout最重要的屬性 scrollDirection沒有在這里進行設(shè)置淆两,其包含兩種流式布局的方向:

typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) {
    UICollectionViewScrollDirectionVertical,
    UICollectionViewScrollDirectionHorizontal
};

其默認情況下就是垂直方向布局(UICollectionViewScrollDirectionVertical),也就是我們需要的樣子拂酣。

常用代理方法
//section的數(shù)量
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
//對應(yīng)section的item數(shù)量
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
//cell的代理秋冰,只要是繼承自UICollectionViewCell的都可以
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
//header與footer,kind是header與footer的類型區(qū)別
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;

//item的點擊事件
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
//當item即將顯示的時候回觸發(fā)該代理
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);

是不是很眼熟婶熬,怎么跟UITableView的代理那么相似剑勾?是的光坝,他們在設(shè)計上是一樣的,只是UICollectionView更加的靈活甥材。這里就不再對代理進行贅述了盯另,用法跟UITableView是一樣的,更多的的代理可以自行查看洲赵,對于我們要實現(xiàn)的demo鸳惯,有這些就足夠了。

掌握了這些基本的知識點就完全可以用UICollectionView實現(xiàn)一個流式布局的列表叠萍。

2. 列表與展示區(qū)域的聯(lián)動

ins的展示區(qū)域與列表是有聯(lián)動效果的芝发,具體如下:


(1) 當向上滑動列表時,上方展示區(qū)域會上移至只留下40px左右的大小苛谷。且該效果需要在上滑列表直至手指觸碰到展示區(qū)域邊界才會開始觸發(fā)辅鲸。

(2)當下滑動列表時,展示區(qū)域會在列表滑動至頂端時開始跟隨列表滑動腹殿,直至占據(jù)一半的屏幕為止独悴。

(3)展示區(qū)域的下方越有40px高度的區(qū)域有手勢效果,可以將處于隱藏狀態(tài)的展示區(qū)域拖動顯示出來锣尉。


前兩點我們會放在列表(UICollectionView上做)刻炒,我們要去獲取什么數(shù)據(jù)來知道列表滾動了?并且能夠拿到對應(yīng)的數(shù)值自沧?

這個時候會首先想到UICollectionView是繼承自UIScrollView的坟奥,想要知道是否滑動,并且要拿到滑動的數(shù)值拇厢,UIScrollView是提供的有代理的:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;  

該代理只要有滑動的事件發(fā)生都會被觸發(fā)爱谁,我們可以根據(jù)拿到scrollView的contentOffset來獲取滑動的數(shù)據(jù)。然后根據(jù)得到的數(shù)值來推斷出展示區(qū)所應(yīng)該在的位置孝偎。

但是

UIScrollView提供的代理(didScroll)是在滑動發(fā)生后才會觸發(fā)访敌,是已經(jīng)滑動了,這個時候我們?nèi)舾鶕?jù)此數(shù)據(jù)來決定上方展示區(qū)域的位置邪媳,而我們的聯(lián)動是及時的捐顷,若在scrollView后再根據(jù)拿到的數(shù)據(jù)來處理,則會造成顯示異常雨效,例如迅涮,效果(1)中需要展示區(qū)域聯(lián)動時,展示區(qū)域會發(fā)生輕微的抖動(這個肯定不能忍)徽龟。

聯(lián)動效果叮姑,并不僅僅需要滑動數(shù)值,還需要更加詳盡的滑動狀態(tài),如:滑動的開始與結(jié)束传透,滑動距離耘沼,方向等等,以此來觸發(fā)是否需要計算上方展示區(qū)域的位置朱盐。若只是根據(jù)UIScrollView的代理來獲取的確是有點復(fù)雜群嗤,而且也會存在剛提到的【滑動數(shù)據(jù)獲取的時機】問題,我們需要及時的獲取到滑動的數(shù)據(jù)兵琳。

這個時候筆者想到的是這幾個方法:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

在這幾個方法這里來獲取數(shù)據(jù)狂秘,時機肯定是沒問題了,但筆者擔心在此處若操作不當會引發(fā)其他地方的手勢沖突躯肌。但我們并沒有對視圖進行任何操作者春,我們只是獲取到其數(shù)據(jù)即可,不存在手勢沖突的可能性清女。

抱著試試看的心態(tài)钱烟,筆者生成了一個UICollectionView的子類,在子類中實現(xiàn)這些方法嫡丙,以此來獲取所有我們需要的信息拴袭,筆者采用一個Block來進行數(shù)據(jù)的統(tǒng)一回調(diào):

//數(shù)據(jù)的回調(diào),滑動開始的y坐標點(x的坐標對于該demo效果沒有意義),Y軸的移動距離迄沫,以及動作完成的標識稻扬。
typedef void(^ScrollToTopMoreBlock)(CGFloat startY, CGFloat moveY, BOOL isEnd);

而整個子類的功能,都只是在圍繞獲取滑動的數(shù)據(jù)來進行的:

- (BOOL)touchesShouldBegin:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view {
    //滑動開始
    UITouch * touch = [touches anyObject];
    _touchPoint = [touch locationInView:self];
    _isTouch = YES;
    return YES;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //滑動開始
    [super touchesBegan:touches withEvent:event];
    UITouch * touch = [touches anyObject];
    _isTouch = YES;
    _touchPoint = [touch locationInView:self];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    //滑動中羊瘩,獲取滑動的距離,并回調(diào)
    UITouch * touch = [touches anyObject];
    _isTouch = YES;
    CGPoint movePoint = [touch locationInView:self];
    if (_moveBlock) {
            _moveBlock(_touchPoint.y, movePoint.y - _touchPoint.y, NO);
        }
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    //滑動結(jié)束
    [self endScrollTopEventWithTouch:[touches anyObject]];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    //滑動結(jié)束
    [self endScrollTopEventWithTouch:[touches anyObject]];
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        gestureRecognizer.cancelsTouchesInView = NO;
    }
    return YES;
}

- (void)setScrollBlock:(void(^)(CGFloat startY, CGFloat moveY, BOOL isEnd))block {
    _moveBlock = block;
}

- (void)endScrollTopEventWithTouch:(UITouch *)touch {
    if (_moveBlock) {
        _isTouch = YES;
        CGPoint movePoint = [touch locationInView:self];
        _moveBlock(_touchPoint.y, movePoint.y - _touchPoint.y, YES);
    }
}

有了這些實時的數(shù)據(jù)盼砍,我們就可以完成我們的聯(lián)動效果尘吗。聯(lián)動的邏輯稍稍有點復(fù)雜,這里就不列舉出來了···可以先自行實現(xiàn)以下看看浇坐,若沒有什么思路睬捶,可以參考下筆者的方式。筆者的方式稍顯笨拙近刘,并不適宜在此處展開擒贸。感興趣的朋友可以看看Demo。

其他 CollectionView 相關(guān)內(nèi)容:

1. iOS 自定義圖片選擇器 3 - 相冊列表的實現(xiàn)
2. UICollectionView自定義布局基礎(chǔ)
3. UICollectionView自定義拖動重排
4. iOS13 中的 CompositionalLayout 與 DiffableDataSource
5. iOS14 中的UICollectionViewListCell觉渴、UIContentConfiguration 以及 UIConfigurationState

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載介劫,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末案淋,一起剝皮案震驚了整個濱河市座韵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖誉碴,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宦棺,死亡現(xiàn)場離奇詭異,居然都是意外死亡黔帕,警方通過查閱死者的電腦和手機代咸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來成黄,“玉大人呐芥,你說我怎么就攤上這事】” “怎么了贩耐?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長厦取。 經(jīng)常有香客問我潮太,道長,這世上最難降的妖魔是什么虾攻? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任铡买,我火速辦了婚禮,結(jié)果婚禮上霎箍,老公的妹妹穿的比我還像新娘奇钞。我一直安慰自己,他們只是感情好漂坏,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布景埃。 她就那樣靜靜地躺著,像睡著了一般顶别。 火紅的嫁衣襯著肌膚如雪谷徙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天驯绎,我揣著相機與錄音完慧,去河邊找鬼。 笑死剩失,一個胖子當著我的面吹牛屈尼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拴孤,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼脾歧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了乞巧?” 一聲冷哼從身側(cè)響起涨椒,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚕冬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體免猾,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年囤热,在試婚紗的時候發(fā)現(xiàn)自己被綠了猎提。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡旁蔼,死狀恐怖锨苏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棺聊,我是刑警寧澤伞租,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站限佩,受9級特大地震影響葵诈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祟同,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一作喘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晕城,春花似錦泞坦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至滤蝠,卻和暖如春李根,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背几睛。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留粤攒,地道東北人所森。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像夯接,于是被迫代替她去往敵國和親焕济。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容