模仿IMDb和格瓦拉的圖片集混合排布展示效果.


iMDb和格瓦拉的圖片集排布,自己抽時間進(jìn)行了一次嘗試,使用自定義UICollectionViewFlowLayout來進(jìn)行布局,目前點(diǎn)擊事件效果還沒有完善,下面是一張相關(guān)計算圖,字比較丑,別噴我.

下面貼出來自定義布局的.h和.m文件,第一次寫,不會使用高端工具,以后會繼續(xù)加油,有好用的工具,可以推薦給我,在這里感謝大家了.

FJSPicMixCollectionViewLayout.h

#import "BQImageModel.h"

@interface FJSPicMixCollectionViewLayout : UICollectionViewFlowLayout

@property (nonatomic,strong)NSMutableArray * modelArray;

@property (nonatomic,assign)BOOL isHeaderRefresh;/**< 區(qū)分是上拉加載還是下拉刷新 */

@end


#import "FJSPicMixCollectionViewLayout.h"

@interface FJSPicMixCollectionViewLayout ()

@property (nonatomic,assign)CGFloat contentHeight;/**< 總體高度 */@property (nonatomic,strong)NSMutableArray * attributesArray;/**< 存放所有布局的array *///

@property (nonatomic,assign)NSInteger lastArrayCount;/**< 記錄上一次的數(shù)組整體個數(shù),為了上拉刷新的時候不需要重新計算之前數(shù)組的位置,進(jìn)行性能優(yōu)化 */

@end

@implementation FJSPicMixCollectionViewLayout

/* 所以我們的思路是在- (void)prepareLayout;方法中算出所有item的frame,并賦值給當(dāng)前item的? UICollectionViewLayoutAttributes卒落。用圖片的形式比較直觀: */

-(void)prepareLayout{?

?? [super prepareLayout];? ?

?if (self.isHeaderRefresh) {? ? ? ?

?//初始化保存所有item attributes的數(shù)組? ? ??

? self.attributesArray = [NSMutableArray array];? ? ? ??

self.contentHeight = 0.f;//新添加??

? }? ?

?[self getwholeRowFrame];

}

- (void)getwholeRowFrame{? ?

?//設(shè)置一個寬度來記錄和判斷圖片是否換行.? ?

?CGFloat width = 0.f;? ?

?//保存同一行圖片的所有尺寸比例和,用來計算這一行圖片的高度? ?

?CGFloat scaleSum = 0.f;? ?

?//如果之前的布局?jǐn)?shù)組中有數(shù)據(jù),上拉加載就從下一行,新來的model開始計算,不需要考慮之前最后一行是否已經(jīng)超出屏幕,正常是要從倒數(shù)第一行重新計算,但是圖片可能會有所閃動,體驗(yàn)不好.? ??

NSInteger beginIndex = self.attributesArray.count?self.attributesArray.count:0;??

? NSInteger currentIndex = beginIndex;? ??

//不需要擔(dān)心最后如果只有一張圖的話,沒有匹配如何顯示,因?yàn)楸闅v的次數(shù)和圖片的數(shù)量相同.? ? NSLog(@"beginIndex == %ld",beginIndex);? ?

?for (NSInteger i = beginIndex; i < self.modelArray.count; i++) {??

? ? ? BQImageModel * model = [self.modelArray objectAtIndex:i];? ? ?

?? width = width + model.width;? ? ?

?? scaleSum = scaleSum + model.whScale;??

? ? ? //換行之后需要重新清空累計的寬度 同時保存下一個currentIndex從第幾行開始.? ? ?

?? //累計圖片寬度,如果寬度超過了屏寬減去間距,則換行(這種方式存在一定的問題,依賴于圖片的原始高度來進(jìn)行排布,不過服務(wù)器沒法根據(jù)客戶端來進(jìn)行圖片匹配,所以繼續(xù)研究了格瓦拉之后,找到了它有一個圖片最大高度,即屏幕的一半,所以采用比例和的方式來進(jìn)行約束.)? ? ??

? if (scaleSum >= 2.0) {? ? ? ? ?

?? [self setAttributesFromCurrentIndex:currentIndex DestionIndex:i scaleSum:scaleSum];? ? ? ? ? ? //換行之后需要重新清空累計的寬度 同時保存下一個currentIndex從第幾行開始.? ? ? ??

? ? width = 0.f;? ? ? ? ??

? scaleSum = 0.f;? ? ? ? ? ??

currentIndex = i + 1;? ? ? ??

}else? ? ? ? {? ? ? ? ?

?? //如果是最后一行并沒有滿足超過屏寬,則將當(dāng)前幾個視圖進(jìn)行計算,鋪滿屏幕? ?

?? ? ? ? if (i == self.modelArray.count - 1) {? ? ? ?

?? ? ? ? [self setAttributesFromCurrentIndex:currentIndex DestionIndex:i scaleSum:scaleSum]; ? ? ? ? ?}? ? ? ? }? ? }? ? }

- (void)setAttributesFromCurrentIndex:(NSInteger)currnetIndex DestionIndex:(NSInteger)destionIndex scaleSum:(CGFloat)scaleSum{? ?

?//根據(jù)公式計算出該行的高度? ?

?CGFloat height = (ScreenWidth - (destionIndex - currnetIndex) * self.minimumInteritemSpacing) / scaleSum;??

? //均分的寬度,注意:四舍五入成整數(shù)? ?

?height = roundf(height);??

? NSLog(@"從第%ld個到第%ld個,高度為%f",currnetIndex,destionIndex,height);? ?

?for (NSInteger i = currnetIndex; i <= destionIndex; i++) {? ? ? ?

?//給attributes.frame 賦值售貌,并存入 self.itemsAttributes? ? ??

? BQImageModel * model = [self.modelArray objectAtIndex:i];? ? ? ? //根據(jù)計算出來的高度來根據(jù)圖片比例計算出寬度? ? ??

? CGFloat width = height * model.whScale;? ? ? ?

?UICollectionViewLayoutAttributes * oldAttributes;? ? ?

?? /*如果不是這一行的第一個圖片,需要獲取上一張圖片的UICollectionViewLayoutAttributes,用來計算當(dāng)前的圖片的x值.為什么不使用? ? ? ? NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];? ? ? ? UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];方法來獲取上一個內(nèi)容呢?因?yàn)閮?nèi)容為空,都保存到數(shù)組self.attributesArray中了,所以直接獲取.? ? ? ? */? ? ??

? if (i > currnetIndex) {? ? ? ? ??

? NSInteger oldIndex = i - 1;? ? ? ? ? ?

?oldAttributes = [self.attributesArray objectAtIndex:oldIndex];? ? ?

?? }? ? ? ?

?NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];? ? ? ? UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];? ? ? ?

?/*獲取當(dāng)前的對應(yīng)UICollectionViewLayoutAttributes,進(jìn)行修改,然后保存到數(shù)組中? ? ? ? x: 根據(jù)同一行,前一個視圖進(jìn)行累計,同時加上self.minimumInteritemSpacing? ? ? ? y: 使用全局的屬性記錄.? ? ? ? width和height都有計算好了.? ? ? ? */? ? ?

?? CGFloat orignX = oldAttributes?CGRectGetMaxX(oldAttributes.frame) + self.minimumInteritemSpacing:0;? ? ??

? if (destionIndex == currnetIndex && self.modelArray.count - 1 == currnetIndex && model.whScale < 2.0) {? ? ? ??

? ? attributes.frame = CGRectMake(orignX, self.contentHeight, ScreenWidth, ScreenWidth * 0.5);? ? ? ? ? ? height = ScreenWidth * 0.5;? ? ?

?? }else? ? ? ? {? ? ? ? ? ?

?attributes.frame = CGRectMake(orignX, self.contentHeight, width, height);? ??

? ? }? ? ? ??

//? ? ? ? NSLog(@"oldAttributes == %f\nself.contentHeight == %f\nwidth == %f\nheight == %f",CGRectGetMaxX(oldAttributes.frame),self.contentHeight,width,height);? ? ?

?? //? ? ? ? NSLog(@"第%ld個到第%ld個在一行",currnetIndex,destionIndex);? ? ? ? [self.attributesArray addObject:attributes];?

?? }? ??

//累加記錄高度的??

? self.contentHeight = self.contentHeight + height + self.minimumLineSpacing;}-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect

{

NSLog(@"我觸發(fā)了");

NSLog(@"%ld",self.attributesArray.count);

return self.attributesArray;

}

-(CGSize)collectionViewContentSize{ ?

? //使用數(shù)組中最后一個布局來進(jìn)行滾動內(nèi)容的高度,而不是self.contentHeight,原因是需要判斷是否是最后一個圖片的那一行,如果是不需要累加self.minimumLineSpacing.? ? UICollectionViewLayoutAttributes * lastAttributes = [self.attributesArray lastObject];? ? ? ? return CGSizeMake(ScreenWidth, CGRectGetMaxY(lastAttributes.frame));}

//#pragma mark -- 返回collectionView的內(nèi)容的尺寸//-(CGSize)collectionViewContentSize//{////}

#pragma mark -- UICollectionViewLayoutAttributes可以是cell渊涝,追加視圖或裝飾視圖的信息囤锉,通過不同的UICollectionViewLayoutAttributes初始化方法可以得到不同類型的UICollectionViewLayoutAttributes:

#pragma mark -- 返回對應(yīng)于indexPath的位置的cell的布局屬性

//-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath

//{

//

//}

//

#pragma mark -- 返回對應(yīng)于indexPath的位置的追加視圖的布局屬性丈莺,如果沒有追加視圖可不重載

//-(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath

//{

//

//}

//

#pragma mark -- 返回對應(yīng)于indexPath的位置的裝飾視圖的布局屬性屯耸,如果沒有裝飾視圖可不重載

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

//{

//

//}

//

//-(UICollectionViewLayoutAttributes *)layoutAttributesForInteractivelyMovingItemAtIndexPath:(NSIndexPath *)indexPath withTargetPosition:(CGPoint)position

//{

//

//}

/*

- 當(dāng)邊界發(fā)生改變時拐迁,是否應(yīng)該刷新布局。如果YES則在邊界變化(一般是scroll到其他地方)時疗绣,將重新計算需要的布局信息线召。

另外需要了解的是,在初始化一個UICollectionViewLayout實(shí)例后多矮,會有一系列準(zhǔn)備方法被自動調(diào)用缓淹,以保證layout實(shí)例的正確。

首先塔逃,-(void)prepareLayout將被調(diào)用讯壶,默認(rèn)下該方法什么沒做,但是在自己的子類實(shí)現(xiàn)中湾盗,一般在該方法中設(shè)定一些必要的layout的結(jié)構(gòu)和初始需要的參數(shù)等伏蚊。

之后,-(CGSize) collectionViewContentSize將被調(diào)用格粪,以確定collection應(yīng)該占據(jù)的尺寸躏吊。注意這里的尺寸不是指可視部分的尺寸,而應(yīng)該是所有內(nèi)容所占的尺寸帐萎。collectionView的本質(zhì)是一個scrollView颜阐,因此需要這個尺寸來配置滾動行為。

接下來-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect被調(diào)用吓肋,這個沒什么值得多說的。初始的layout的外觀將由該方法返回的UICollectionViewLayoutAttributes來決定瑰艘。

另外是鬼,在需要更新layout時,需要給當(dāng)前l(fā)ayout發(fā)送 -invalidateLayout紫新,該消息會立即返回均蜜,并且預(yù)約在下一個loop的時候刷新當(dāng)前l(fā)ayout,這一點(diǎn)和UIView的setNeedsLayout方法十分類似芒率。在-invalidateLayout后的下一個collectionView的刷新loop中囤耳,又會從prepareLayout開始,依次再調(diào)用-collectionViewContentSize和-layoutAttributesForElementsInRect來生成更新后的布局。

*/

//-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds

//{

//? ? return YES;

//}

@end

更多的布局方式,在慢慢的添加,有需要你可以在這里獲取到整個項(xiàng)目.

https://github.com/BestJoker/FJSPicMixCollectionViewLayout.git

如果對你的思路有一定的幫助,請別吝嗇你的星星.預(yù)祝大家編程愉快.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末充择,一起剝皮案震驚了整個濱河市德玫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌椎麦,老刑警劉巖宰僧,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異观挎,居然都是意外死亡琴儿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門嘁捷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來造成,“玉大人,你說我怎么就攤上這事雄嚣∩故海” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵现诀,是天一觀的道長夷磕。 經(jīng)常有香客問我,道長仔沿,這世上最難降的妖魔是什么坐桩? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮封锉,結(jié)果婚禮上绵跷,老公的妹妹穿的比我還像新娘。我一直安慰自己成福,他們只是感情好碾局,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奴艾,像睡著了一般净当。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蕴潦,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天像啼,我揣著相機(jī)與錄音,去河邊找鬼潭苞。 笑死忽冻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的此疹。 我是一名探鬼主播僧诚,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼遮婶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了湖笨?” 一聲冷哼從身側(cè)響起旗扑,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赶么,沒想到半個月后肩豁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辫呻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年清钥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片放闺。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡祟昭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出怖侦,到底是詐尸還是另有隱情篡悟,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布匾寝,位于F島的核電站搬葬,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏艳悔。R本人自食惡果不足惜急凰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望猜年。 院中可真熱鬧抡锈,春花似錦、人聲如沸乔外。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杨幼。三九已至撇簿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間差购,已是汗流浹背补疑。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歹撒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓诊胞,卻偏偏與公主長得像暖夭,于是被迫代替她去往敵國和親锹杈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

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