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ù)祝大家編程愉快.