iOS中TableView內(nèi)嵌套CollectionView動(dòng)態(tài)高度的實(shí)現(xiàn)(類似表單或圖片分享界面)

在有社交分享平臺(tái)屬性的app中渣叛,我們經(jīng)嘲嗝校看見類似有tableview中多圖展示希停。不管是發(fā)布的表單界面中,還是社交動(dòng)態(tài)的時(shí)間線的界面中署隘,都需要根據(jù)圖片數(shù)量動(dòng)態(tài)變化界面宠能。最近剛好寫了一個(gè)這樣的界面,花了點(diǎn)時(shí)間寫了個(gè)Demo總結(jié)一下磁餐,希望可以幫助有需要的人违崇。實(shí)現(xiàn)Demo效果如下圖。

collectionintable.gif

實(shí)現(xiàn)原理分析

初步思路是在tableview的cell中內(nèi)嵌一個(gè)collecitonview,collectionview高度動(dòng)態(tài)變化并是collectionview所在的tableview的cell的高度動(dòng)態(tài)變化亦歉⌒衾耍總結(jié)起來我們需要這幾件事:

1.實(shí)現(xiàn)一個(gè)tableview并自定義一種tableviewcell并實(shí)現(xiàn)高度自適應(yīng)

2.在tableviewcell中實(shí)現(xiàn)collectionview并實(shí)現(xiàn)高度動(dòng)態(tài)變化

3.自定義collectionviewcell中實(shí)現(xiàn)按鈕點(diǎn)擊事件(如刪除,跳轉(zhuǎn))肴楷,數(shù)據(jù)展示操作

1.實(shí)現(xiàn)tableview和自定義tableviewcell

tableview就很簡單水由,storyboard或者代碼寫一下都可以。實(shí)現(xiàn)數(shù)據(jù)源協(xié)議啥的赛蔫,很普通砂客。要實(shí)現(xiàn)tableviewcell的高度自適應(yīng),一般來說有兩種方式呵恢,一種是用iOS7后支持的cell的estimatedRowHeight和iOS8后支持的self-sizing cells(兩者差不多鞠值,iOS8更完善一些),另一種是用孫源大神的第三方開源庫渗钉,可以看這篇文章彤恶,兩者共同之處都是需要設(shè)置cell里contenview的元素對(duì)cell的contenview的四個(gè)邊的布局約束,換言之要讓cell里的元素把cell四邊“撐”起來鳄橘。Demo里使用了原生的self-sizing cells來高度自適應(yīng)声离,需要下面兩句代碼。

self.tableview.estimatedRowHeight = 100.f;//數(shù)字為大致估算高度瘫怜,比如有些50有些100可以估算75左右
self.tableview.rowHeight = UITableViewAutomaticDimension;

如果tableview是用代碼創(chuàng)建的术徊,那么rowheight屬性的默認(rèn)參數(shù)就是UITableViewAutomaticDimension,不需要第二行代碼鲸湃。而在storyboard或xib里拖的默認(rèn)rowheight是拖的storyboard屬性菜單里的赠涮,需要更改為UITableViewAutomaticDimension

接著自定義一個(gè)CDZTableViewCell暗挑,并用xib拖上一個(gè)collectionview并設(shè)置對(duì)contenview的autolayout約束笋除。用Masonry之類的第三方布局庫或原生進(jìn)行代碼約束也可以。然后在tableview里用registerNib方法注冊(cè)一下自定義的cell炸裆。

因?yàn)門ableView執(zhí)行reloadData方法時(shí)株憾,會(huì)把所有cell從VisableCells池中移除,并從同一復(fù)用標(biāo)識(shí)的復(fù)用池中取出Cell加入視圖中晒衩,這個(gè)時(shí)候cell的高度已經(jīng)確定好嗤瞎,但indexPath的row順序有可能不是原來的,所以不能復(fù)用听系。也就是雖然每一個(gè)Cell功能一致贝奇,但是由于高度和里面圖片數(shù)量不一樣,并不能互相復(fù)用靠胜。在Storyboard中可以用靜態(tài)cell分別放入collectionView實(shí)現(xiàn)掉瞳。這里為了不重復(fù)放collectionView用了代碼書寫TableView毕源。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    NSString *identifer = [NSString stringWithFormat:@"CDZTableViewCell%ld",indexPath.row];//唯一復(fù)用標(biāo)識(shí),相當(dāng)于不復(fù)用
    CDZTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer];
    if (!cell) {
        cell  = [CDZTableViewCell.alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifer];
    }
    cell.delegate = self;
    return cell;
}

當(dāng)然要實(shí)現(xiàn)動(dòng)態(tài)高度變化陕习,我們還需要讓cell在合適的時(shí)候通知tableview應(yīng)該更新數(shù)據(jù)和布局了霎褐,即調(diào)用tableview自身的reloadData方法。這里我用delegate實(shí)現(xiàn)该镣,通知的話也可以冻璃,但是通知的要明確接受者和發(fā)送者的對(duì)應(yīng)關(guān)系,不然有些情況會(huì)接受對(duì)象不明確(比如實(shí)現(xiàn)了兩個(gè)類似的tableview在視圖里)损合。在cell的頭文件里創(chuàng)建delegate省艳。

@protocol CDZTableViewCellDelegate<NSObject>
- (void)didChangeCell:(UITableViewCell *)cell;
@end
@interface CDZTableViewCell : UITableViewCell
@property (nonatomic,assign) id<CDZTableViewCellDelegate> delegate;
@end

然后讓tableview遵守CDZTableViewCellDelegate并在tableview的datasource或delegate方法里設(shè)置cell的delegate,比如

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    //......
    cell.delegate = self;
    //......
}

也可以在delegate里的-(void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath里設(shè)置(感覺這個(gè)方法更好些嫁审,datasource應(yīng)該處理數(shù)據(jù)相關(guān)的跋炕,而且有時(shí)候會(huì)復(fù)用抽離出來)。

2.實(shí)現(xiàn)collectionview和自定義collectionviewcell

collectionview在約束上律适,除了實(shí)現(xiàn)collecitionview對(duì)tableviewcell的上下左右約束外辐烂,還要實(shí)現(xiàn)其高度約束,因?yàn)楸举|(zhì)collectionview是scrollview的子類捂贿,實(shí)現(xiàn)scollview的autolayout其實(shí)就是需要實(shí)現(xiàn)對(duì)其撐起來contentview的約束纠修,而動(dòng)態(tài)實(shí)現(xiàn)contentview的高度,則需要我們更新contenview的高度布局約束的值眷蜓。collectionview也是類似的,我們先設(shè)定一個(gè)collectionview的高度約束胎围,再在數(shù)據(jù)更新時(shí)更新高度約束吁系。

這里再提一句,collectionview本身和tableview類似白魂,系統(tǒng)本身也有self-sizing cells的api汽纤,如下:

self.collectionViewFlowLayout.estimatedItemSize = CGSizeMake(125, 100);
self.collectionViewFlowLayout.itemSize = UICollectionViewFlowLayoutAutomaticSize;

同樣也要實(shí)現(xiàn)collectionviewcell里元素對(duì)cell四邊的約束,“撐”起來自動(dòng)計(jì)算高度福荸,但是不知道為何用了之后在一個(gè)cell的時(shí)候會(huì)莫名其妙居中蕴坪,且cell的indexPath有些錯(cuò)亂,根據(jù)cell的indexPath找出來的cell是錯(cuò)誤的敬锐,不知道是啥問題背传,若有人知道可以告訴一聲囧。

這里就不用collectionview的大小自適應(yīng)了台夺,常規(guī)的用設(shè)置itemsize的大小就好了径玖。在tableviewcellawakeFromNib方法中,我們讓collectionviewreloadData更新一下高度布局約束為collectionview的真實(shí)高度并調(diào)用剛才自定義的delegate去讓tableviewreloadData從而讓tableview的cell高度自適應(yīng)颤介。

- (void)reloadCell{
    [self.collectionView reloadData];
    [self.collectionView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(@(self.collectionView.collectionViewLayout.collectionViewContentSize.height));
    }];
    [self.delegate didChangeCell:self];
}

collectionview的真實(shí)大小其實(shí)是他的layout對(duì)象的collectionviewcontentsize的值梳星。所以我們需要在每次數(shù)據(jù)發(fā)生改變時(shí)赞赖,更新一下高度約束的值。

還有就是實(shí)現(xiàn)collectionview的delegate冤灾,點(diǎn)擊后執(zhí)行操作前域,在賦值在cell對(duì)應(yīng)的model里等操作(比如調(diào)用相冊(cè)等)。注意當(dāng)點(diǎn)擊最后一個(gè)collectioncell時(shí)韵吨,要在它的后面插入一個(gè)數(shù)據(jù)匿垄,也就是說,最后一個(gè)總是會(huì)保持在最后学赛。

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    //載入數(shù)據(jù)年堆,如圖片等
    CDZCollectionViewItem *item = [CDZCollectionViewItem new];
    item.image = [UIImage imageNamed:@"example"];
    if ((indexPath.row == self.itemsArray.count - 1)) {
        [self.itemsArray insertObject:item atIndex:self.itemsArray.count - 1];
    }
    else{
        self.itemsArray[indexPath.row] = item;
    }
    [self reloadCell];
}

3.自定義collectionviewcell并實(shí)現(xiàn)刪除按鈕

先自定義一個(gè)CDZCollectionViewCell,并用xib拖上一個(gè)imageview用于展示圖片盏浇,一個(gè)button用于點(diǎn)擊關(guān)閉cell变丧。并定義好解析item的方法。item中設(shè)定一個(gè)delBtnHidden屬性用于設(shè)定cell里的button是否隱藏绢掰。

- (void)setItem:(CDZCollectionViewItem *)item{
    //  解析需要的數(shù)據(jù)
    self.imageView.image = item.image;
    self.delButton.hidden = item.delBtnHidden;
}

并定義一個(gè)delegate用于將按鈕點(diǎn)擊事件回傳給collectionview并刪除數(shù)據(jù)痒蓬。

@protocol CDZCollectionCellDelegate<NSObject>
- (void)didDelete:(UICollectionViewCell *)cell;
@end
  
@interface CDZCollectionViewCell : UICollectionViewCell
@property (strong, nonatomic) CDZCollectionViewItem *item;
@property (assign, nonatomic) id<CDZCollectionCellDelegate> delegate;
@end

并在點(diǎn)擊按鈕的事件中調(diào)用delegate,把cell本身傳回tableview滴劲,從而找到cell對(duì)應(yīng)的item攻晒。

- (IBAction)delCell:(UIButton *)sender{
    if ([self.delegate respondsToSelector:@selector(didDelete:)]){
        [self.delegate didDelete:self];
    }
}

然后使collectionview所在的tableviewcell遵守CDZCollectionCellDelegate。并和之前一樣班挖,在collectionview的datasource或delegate中設(shè)置delegate鲁捏。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    //......
    cell.delegate = self;
    //......
}

并調(diào)用cell的delegate的方法,通過cell去找到對(duì)應(yīng)的indexPath

- (void)didDelete:(UICollectionViewCell *)cell{
    NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
    [self.itemsArray removeObjectAtIndex:indexPath.row];
    [self reloadCell];
}

第一個(gè)cell是沒有關(guān)閉按鈕的萧芙,那么只要把第一個(gè)item的delBthHidden屬性設(shè)為YES就可以了给梅。

最后

所有源碼和Demo

如果您覺得有幫助,不妨給個(gè)star鼓勵(lì)一下,歡迎關(guān)注&交流

有任何問題歡迎評(píng)論私信或者提issue

QQ:757765420
Email:nemocdz@gmail.com
Github:Nemocdz
微博:@Nemocdz

謝謝觀看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市双揪,隨后出現(xiàn)的幾起案子动羽,更是在濱河造成了極大的恐慌,老刑警劉巖渔期,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件运吓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡疯趟,警方通過查閱死者的電腦和手機(jī)拘哨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來信峻,“玉大人宅静,你說我怎么就攤上這事≌酒郏” “怎么了姨夹?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵纤垂,是天一觀的道長。 經(jīng)常有香客問我磷账,道長峭沦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任逃糟,我火速辦了婚禮吼鱼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绰咽。我一直安慰自己菇肃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布取募。 她就那樣靜靜地躺著琐谤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪玩敏。 梳的紋絲不亂的頭發(fā)上斗忌,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音旺聚,去河邊找鬼织阳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛砰粹,可吹牛的內(nèi)容都是我干的唧躲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼碱璃,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼弄痹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起厘贼,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤界酒,失蹤者是張志新(化名)和其女友劉穎圣拄,沒想到半個(gè)月后嘴秸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡庇谆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年岳掐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饭耳。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡串述,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寞肖,到底是詐尸還是另有隱情纲酗,我是刑警寧澤衰腌,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站觅赊,受9級(jí)特大地震影響右蕊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吮螺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一饶囚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸠补,春花似錦萝风、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至被因,卻和暖如春卿拴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梨与。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工堕花, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粥鞋。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓缘挽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親呻粹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子壕曼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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