Audio

ColectionView 的裝飾視圖自定義布局揍堕,糊一張帶陰影效果的窝剖,Swift 5



#### 重點(diǎn):

一沃饶,

裝飾視圖 Decoration View 棠隐,蘋果的例子是一個(gè) cell 貼一張背景圖石抡。

實(shí)際上,一個(gè) section ,貼一張背景圖助泽,可以的啰扛。

蘋果設(shè)計(jì)的非常靈活嚎京,基本上背景圖想怎么糊上去,就怎么糊

實(shí)踐中發(fā)現(xiàn)

二隐解,

設(shè)置 Decoration View 鞍帝,手寫 UICollectionViewFlowLayout ( 或 UICollectionViewLayout ),是寫死的煞茫。

布局顯示帕涌,一般有一個(gè)網(wǎng)絡(luò)請(qǐng)求。數(shù)據(jù)請(qǐng)求回來前续徽,走自定義的 layout , 到具體的 indexpath, 訪問手工設(shè)置有蚓曼,因?qū)嶋H不存在,崩钦扭。

因?yàn)闆]有網(wǎng)絡(luò)請(qǐng)求回?cái)?shù)據(jù)辟躏,實(shí)際的 section 數(shù)量一般為 0.

需判斷一下。

三土全,

無關(guān) Decoration View 捎琐。

做了一個(gè)商品首頁的需求,UICollectionView 七層樓裹匙,每層樓都不一定有瑞凑,樓層順序也不一定。

如果寫 if else 概页,就要命籽御。通過字典配置,解決

<hr>

詳細(xì)介紹:

-

配圖說明:

“第一個(gè) cell" , 是第一個(gè) section, 只有 1 個(gè) item

“第二個(gè) cell" , 是第二個(gè) section, 有 5 個(gè) item

![](https://user-gold-cdn.xitu.io/2019/1/6/168226dd016c7b0a?w=750&h=1334&f=png&s=508639)

第一點(diǎn)惰匙,一個(gè) section ,貼一張背景圖

設(shè)置背景圖的區(qū)域技掏,糊上去,end

具體烹飪教程如下:

裝飾視圖是 UICollectionViewLayout 的功能项鬼,不是 UICollectionView 的哑梳。

UICollectionView 的方法、代理方法 (delegate, datasource)都不涉及裝飾視圖绘盟。

UICollectionView 對(duì)裝飾視圖一無所知鸠真,UICollectionView 按照 UICollectionViewLayout 設(shè)置的渲染。

要用裝飾視圖龄毡,就要自定制 UICollectionViewLayout吠卷,也就是 UICollectionViewLayout 的子類。這個(gè) UICollectionViewLayout 子類沦零,可以添加屬性祭隔、代理屬性,通過設(shè)置代理協(xié)議方法路操,來自定制裝飾視圖疾渴。

本文 Demo 舉的例子是添加一個(gè)裝飾視圖背景圖片千贯。

(沒有涉及使用代理,設(shè)置協(xié)議方法程奠,進(jìn)一步控制裝飾視圖)

簡(jiǎn)要說來,自定制的 layout 子類祭钉,實(shí)現(xiàn)一個(gè)裝飾視圖瞄沙,五步:

步驟 1,

要有 Decoration View 文件慌核。

先創(chuàng)建一個(gè) UICollectionResuableView 的子類, 這個(gè)就是具體的裝飾視圖

```

@interface FrontDecorationReusableView()

// 裝飾視圖距境,里面就一張圖片

@property (nonatomic, strong) UIImageView * imageView;

@end

@implementation FrontDecorationReusableView

- (instancetype)initWithFrame:(CGRect)frame{

? ? if (self = [super initWithFrame:frame]){

? ? ? ? self.backgroundColor = UIColor.whiteColor;

? ? ? ? _imageView = [[UIImageView alloc] init];

? ? ? ? [self addSubview: _imageView];

? ? ? ? // 使用了 masonry 布局

? ? ? ? [_imageView mas_makeConstraints:^(MASConstraintMaker *make) {

? ? ? ? ? ? make.edges.mas_equalTo(self);

? ? ? ? }];

? ? }

? ? return self;

}

```

步驟 2,

layout 中注冊(cè)裝飾視圖垮卓。

有了裝飾視圖垫桂,組裝在一起 (wire it up)

自定制的 layout 子類中,注冊(cè) UICollectionResuableView 的子類粟按,也就是裝飾視圖诬滩。

調(diào)用 `- (void)registerClass:(nullable Class)viewClass forDecorationViewOfKind:(NSString *)elementKind;` 方法。

一般在 `- (void)prepareLayout ` 方法中注冊(cè)灭将。

```

- (void)prepareLayout {

? ? [super prepareLayout];

? ? [self registerClass: FrontDecorationReusableView.class forDecorationViewOfKind: FDRFrontDecorationReusableView];

}

```

步驟 3疼鸟,

設(shè)置裝飾視圖的位置。

`- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath` 方法庙曙,設(shè)置裝飾視圖 UICollectionResuableView 的位置空镜,因?yàn)樵摲椒ǚ祷亓搜b飾視圖的布局屬性。

`+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath;` 方法捌朴,構(gòu)建布局屬性吴攒,并作相關(guān)的配置。

先設(shè)置裝飾視圖的具體位置砂蔽,

```

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{


? ? if (elementKind == FDRFrontDecorationReusableView && indexPath.section == 1) {

? ? ? ? DecorationLayoutAttributes * attributes = [DecorationLayoutAttributes layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView withIndexPath: indexPath];

? ? ? ? // 通過屬性洼怔,外部設(shè)置裝飾視圖的實(shí)際圖片 ( 后有介紹 )

? ? ? ? attributes.imgUrlStr = self.imgUrlString;

? ? ? // 這里,裝飾視圖的位置是固定的

? ? ? ? CGFloat heightOffset = 16;

? ? ? ? attributes.frame = CGRectMake(0, KScreenWidth * 0.5 - heightOffset, KScreenWidth, 102 + heightOffset);

? ? ? ? attributes.zIndex -= 1;

? ? ? ? return attributes;

? ? }

? ? return nil;

}

```

步驟 4左驾,

重寫 `- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect` 方法茴厉,

該方法會(huì)返回給定區(qū)域內(nèi),所有視圖 ( 格子視圖什荣、補(bǔ)充視圖(header \ footer)矾缓、裝飾視圖 ) 的布局屬性。

這里要糊上裝飾視圖稻爬,`layoutAttributesForElementsInRect:`返回的布局屬性數(shù)組嗜闻,需含有調(diào)用 `- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath` 方法中設(shè)置的布局屬性。

這一步比較關(guān)鍵桅锄,collectionView 得到了足夠的信息琉雳,顯示裝飾視圖样眠。

當(dāng) collectionView 調(diào)用 `layoutAttributesForElementsInRect:`,他會(huì)提供每一種裝飾視圖的布局屬性翠肘。

collectionView 對(duì)裝飾視圖是隔離的檐束,一無所知∈叮看到的 collectionView 的裝飾視圖被丧,是自定制 layout 提供的。

步驟 2中绪妹,注冊(cè)了裝飾視圖甥桂,即創(chuàng)建了自定制的裝飾視圖實(shí)例。collectionView 會(huì)根據(jù)布局屬性邮旷,放置好黄选。

把上一步設(shè)置的裝飾視圖布局屬性,交給 collectionView 使用

```

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{

? ? NSArray<UICollectionViewLayoutAttributes *> * rawArr = [super layoutAttributesForElementsInRect: rect];

? ? NSMutableArray<UICollectionViewLayoutAttributes *> * array = [[NSMutableArray alloc] initWithArray: rawArr];

// 避免崩潰 ( 后有介紹 )

? ? NSInteger numberOfSections = [self.collectionView numberOfSections];

? ? if (numberOfSections == 0) {

? ? ? ? return rawArr;

? ? }

? ? UICollectionViewLayoutAttributes * decorationAttrs = [self layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView atIndexPath: [NSIndexPath indexPathForItem: 0 inSection: 1 ]];

? ? if (decorationAttrs && CGRectIntersectsRect(rect, decorationAttrs.frame)) {

? ? ? ? [array addObject: decorationAttrs];

? ? }

? ? return [array copy];

}

```

步驟 5婶肩,

怎么給裝飾視圖傳值办陷?

三步走:

CollcetionView -> layout -> layoutAttributes -> decorationView 裝飾視圖

本文 demo ,是配置具體的裝飾圖片。

先給自定制的 layout 一個(gè)圖片地址屬性律歼,

```

@interface DecorationFlowLayout : UICollectionViewFlowLayout

@property (nonatomic, copy) NSString * imgUrlString;

@end

```

然后想辦法傳過去懂诗,就好了

collectionView 設(shè)置 layout 的圖片 url ,間接控制裝飾視圖的圖片 url

```

......

self.decorationFlowLayout.imgUrlString = @"https://fscdn.zto.com/GetPublicFile/ztPK4Y-WGgWKiRNfkygd3oYQ/thumbnail_747d31f481044bf6a149c7483cd097a5.jpg";

? ? [self.newMainCollectionView reloadData];

}

```

自定制 layout 與裝飾視圖也是隔離的苗膝。創(chuàng)建自定制布局屬性對(duì)象 UICollectionViewLayoutAttributes 來傳值殃恒,相當(dāng)于找了一個(gè)信使。

使用 UICollectionViewLayoutAttributes 的子類辱揭,添加屬性傳值离唐。

```

@interface DecorationLayoutAttributes: UICollectionViewLayoutAttributes

@property (nonatomic, copy) NSString * imgUrlStr;

@end

```

`layoutAttributesForDecorationViewOfKind:` 中配置,

上有提及问窃,

```

DecorationLayoutAttributes * attributes = [DecorationLayoutAttributes layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView withIndexPath: indexPath];

? ? ? ? // 通過屬性亥鬓,外部設(shè)置裝飾視圖的實(shí)際圖片

? ? ? ? attributes.imgUrlStr = self.imgUrlString;

```

最后一小步,

把自定制 LayoutAttributes 的圖片 url 傳遞給裝飾視圖域庇, 靠 `- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes` 方法嵌戈。

當(dāng) collectionView 配置裝飾視圖的時(shí)候,會(huì)調(diào)用該方法听皿。`layoutAttributes` 作為參數(shù)熟呛,取出 `imgUrlStr` 屬性使用,就可以了

```

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{

? ? if ( [layoutAttributes valueForKey: @"imgUrlStr"] && [layoutAttributes isMemberOfClass: NSClassFromString(@"DecorationLayoutAttributes")] ) {

? ? ? ? [self.imageView sd_setImageWithURL_str: [layoutAttributes valueForKey: @"imgUrlStr"]];

? ? }

}

```

<hr>

第二點(diǎn)尉姨,怎么處理庵朝,看一下 [CHTCollectionViewWaterfallLayout](https://github.com/chiahsien/CHTCollectionViewWaterfallLayout)

```

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{

? ? NSArray<UICollectionViewLayoutAttributes *> * rawArr = [super layoutAttributesForElementsInRect: rect];

? ? NSMutableArray<UICollectionViewLayoutAttributes *> * array = [[NSMutableArray alloc] initWithArray: rawArr];

? ? NSInteger numberOfSections = [self.collectionView numberOfSections];

//? ? if (numberOfSections == 0) {

//? ? ? ? return rawArr;

//? ? }

UICollectionViewLayoutAttributes * decorationAttrs = [self layoutAttributesForDecorationViewOfKind: FDRFrontDecorationReusableView atIndexPath: [NSIndexPath indexPathForItem: 0 inSection: 1 ]];

// 因?yàn)檫@一行,崩

// 數(shù)據(jù)請(qǐng)求回來前,不存在實(shí)際的區(qū)間九府。 indexPath 也沒有椎瘟。

```

> 2019-01-05 16:54:59.230718+0800 Improved[31532:238435] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'request for layout attributes for decoration view of kind FrontDecorationReusableView in section 1 when there are only 0 sections in the collection view'

datasource 數(shù)據(jù)源沒設(shè)置,就先返回

判斷一下情況

```

if (numberOfSections == 0) {

? ? ? ? return rawArr;

? ? }

``

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侄旬,一起剝皮案震驚了整個(gè)濱河市肺蔚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌儡羔,老刑警劉巖宣羊,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異笔链,居然都是意外死亡段只,警方通過查閱死者的電腦和手機(jī)腮猖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門鉴扫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人澈缺,你說我怎么就攤上這事坪创。” “怎么了姐赡?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵莱预,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我项滑,道長(zhǎng)依沮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任枪狂,我火速辦了婚禮危喉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘州疾。我一直安慰自己辜限,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布严蓖。 她就那樣靜靜地躺著薄嫡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颗胡。 梳的紋絲不亂的頭發(fā)上毫深,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音毒姨,去河邊找鬼费什。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸳址。 我是一名探鬼主播瘩蚪,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼稿黍!你這毒婦竟也來了疹瘦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤巡球,失蹤者是張志新(化名)和其女友劉穎言沐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酣栈,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡险胰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矿筝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片起便。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖窖维,靈堂內(nèi)的尸體忽然破棺而出榆综,到底是詐尸還是另有隱情,我是刑警寧澤铸史,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布鼻疮,位于F島的核電站,受9級(jí)特大地震影響琳轿,放射性物質(zhì)發(fā)生泄漏判沟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一崭篡、第九天 我趴在偏房一處隱蔽的房頂上張望挪哄。 院中可真熱鬧,春花似錦媚送、人聲如沸中燥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疗涉。三九已至,卻和暖如春吟秩,著一層夾襖步出監(jiān)牢的瞬間咱扣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工涵防, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闹伪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像偏瓤,于是被迫代替她去往敵國(guó)和親杀怠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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