UICollectionView的自定義布局和簡單使用

前言

最近項目中使用了很多UICollectionView蚜迅,發(fā)現(xiàn)對其了解真是太少了,UICollectionViewLayout的自定義布局真的可以實現(xiàn)很多效果秉宿,趁這次機會好好記錄一下知識點荆烈,以前雖然用過,但沒有系統(tǒng)的整理總結(jié)過豹障。這兩天我為UICollectionView做一個整理。包括基本使用焦匈,自定義布局血公,動畫這塊我會的不多后面有機會在再記下來。

UICollectionView的構(gòu)成部分

Cells
Supplementary Views 追加視圖 (類似Header或者Footer)
Decoration Views 裝飾視圖 (用作背景展示)

而在表面下缓熟,由兩個方面對UICollectionView進行支持累魔。其中之一和tableView一樣摔笤,即提供數(shù)據(jù)的UICollectionViewDataSource以及處理用戶交互的UICollectionViewDelegate和布局方面的UICollectionViewDelegateFlowLayout。另一方面垦写,對于cell的樣式和組織方式吕世,由于collectionView比tableView要復(fù)雜得多,因此沒有按照類似于tableView的style的方式來定義梯投,而是專門使用了一個類來對collectionView的布局和行為進行描述命辖,這就是UICollectionViewLayout。

UICollectionView的創(chuàng)建

UICollectionView的創(chuàng)建方式和UITableview相似都是init方法分蓖,不同的是UICollectionView初始化時需要傳入一個UICollectionViewLayout對象用于對其Item進行布局尔艇,這個UICollectionViewLayout對象可以使用系統(tǒng)的UICollectionViewFlowLayout,也可以自定義么鹤,如果想要實現(xiàn)一些炫酷的動畫或者特定的樣式(例如瀑布流)都需要去自定義這個對象终娃。下面是創(chuàng)建UICollectionView的代碼。

-(DWCollectionView*)dwCollectionView{
        if (!_dwCollectionView) {
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
       layout.itemSize = CGSizeMake(100, 30);
        layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);

        _dwCollectionView = [[DWCollectionView alloc] initWithFrame:CGRectMake(0, 64, self.view.bounds.size.width, 400) collectionViewLayout:layout];
        [_dwCollectionView registerClass:[DWCollectionViewCell class] forCellWithReuseIdentifier:@"UICollectionViewCellID"];
        _dwCollectionView.delegate = self;
        _dwCollectionView.dataSource = self;
        _dwCollectionView.backgroundColor = [UIColor yellowColor];
    }
    return _dwCollectionView;
}

UICollectionView的常用屬性和代理方法

UICollectionView有三個代理蒸甜,除去和UITableview一樣的delegate和dataSource尝抖,還有一個ios
10新增的prefetchDataSource,三個代理中前兩個delegate用于處理用戶交互迅皇,dataSource用于提供數(shù)據(jù)源。

dataSource代理方法

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;

// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

delegate常用代理方法

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;

UICollectionViewDelegateFlowLayout

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;

屬性方法

- (void)registerClass:(nullable Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(nullable UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;

///注冊一個headerView或footerView
- (void)registerClass:(nullable Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
- (void)registerNib:(nullable UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;

///獲取指定IndexPath的Cell和Supplementary Views
- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;
- (__kindof UICollectionReusableView *)dequeueReusableSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;

其他還有很多屬性和tableview相似就不多說了

UICollectionView的布局

UICollectionView的布局可以分為三種方式:

1.初始化時傳入的UICollectionViewLayout對象衙熔,通過設(shè)置UICollectionViewLayout對象屬性的值可以設(shè)置item的基本布局登颓,包括大小,間距等红氯。
2.也可以實現(xiàn)UICollectionViewLayoutDelegate協(xié)議對應(yīng)的方法框咙,返回布局需要的值。
3.自定義一個UICollectionViewLayout對象重寫對應(yīng)方法返回自定義的布局痢甘。

注意:同時設(shè)置1和2喇嘱,2的優(yōu)先級更高。

UICollectionViewFlowLayout常用屬性

//item的最小行間距
@property (nonatomic) CGFloat minimumLineSpacing;
//item之間的最小間距塞栅,這個數(shù)據(jù)設(shè)置的是最小的間距者铜,當間距小于這個值時,item就會換行顯示放椰,但是如果你設(shè)置的是10作烟,實際間距是20也是不會換行的只有小于這個值時才會換行。
@property (nonatomic) CGFloat minimumInteritemSpacing;
///item的大小
@property (nonatomic) CGSize itemSize;
//此屬性8.0以后有效砾医,作用:類似一個占位符拿撩,當加載item時會先加載這個size,顯示的時候 根據(jù) 子控件的autolayout 的約束算出自適應(yīng)內(nèi)容的 size如蚜;
  /**
         *1.設(shè)置estimatedItemSize的值压恒,隨便給也行
         *2.對子控件進行約束
         *3.如需進一步操作size可以在cell中重寫preferredLayoutAttributesFittingAttributes方法
  */
 
@property (nonatomic) CGSize estimatedItemSize NS_AVAILABLE_IOS(8_0); // defaults to CGSizeZero - setting a non-zero size enables cells that self-size via -preferredLayoutAttributesFittingAttributes:
//設(shè)置滑動方向
@property (nonatomic) UICollectionViewScrollDirection scrollDirection; // default is UICollectionViewScrollDirectionVertical
//區(qū)頭的size影暴,設(shè)置寬度值無效
@property (nonatomic) CGSize headerReferenceSize;
//區(qū)尾的size,設(shè)置寬度值無效
@property (nonatomic) CGSize footerReferenceSize;
//section之間的上左下右的間距(假設(shè)有三個區(qū)豎向滑動探赫,則top代表每個區(qū)第一行的item距離這個區(qū)上邊的距離型宙,boom代表這個區(qū)的最后一行item距這個區(qū)下邊的距離)不是item之間的上下左右的間距。
@property (nonatomic) UIEdgeInsets sectionInset;

estimatedItemSize屬性的簡單使用

只需設(shè)置estimatedItemSize替代itemSize即可實現(xiàn)簡單的布局,如果自適應(yīng)的label的長度會超出collectionview的寬度那需要label設(shè)置最大的寬度期吓,否則會出現(xiàn)布局錯誤早歇。

@implementation EstimatedItemCollectionViewCell

-(instancetype)initWithFrame:(CGRect)frame{
    if (self =[super initWithFrame:frame]) {
        
   ///設(shè)置contentView上下左右約束為0
        [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.top.bottom.equalTo(self).with.offset(0);
            make.width.mas_equalTo([UIScreen mainScreen].bounds.size.width-20);
        }];
        
        UIImageView *topImageView = [[UIImageView alloc] init];
        topImageView.backgroundColor = [UIColor blueColor];
        [self.contentView addSubview:topImageView];
        
        [topImageView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.top.right.bottom.equalTo(self.contentView).with.offset(0);
//            make.right.equalTo(self.contentView.mas_right).with.offset(-250);
//            make.height.mas_equalTo(200);
        }];
        
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
        label.backgroundColor = [UIColor orangeColor];
        label.textAlignment = NSTextAlignmentCenter;
        label.numberOfLines = 0;
//        [label sizeToFit];
        [self.contentView addSubview:label];
        self.label = label;
        
        //2.設(shè)置好subviews的約束
        [label mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.right.bottom.equalTo(self.contentView).with.offset(0);
//            make.top.equalTo(topImageView.mas_bottom).with.offset(0);
            make.width.mas_lessThanOrEqualTo([UIScreen mainScreen].bounds.size.width-20);
//            make.width.mas_greaterThanOrEqualTo(@(100));

        }];
    }
    return self;
}


///3.如果不需要更多關(guān)于UICollectionViewLayoutAttributes的操作,只是向下面這樣重新賦值給size建議不要重寫
//preferredLayoutAttributesFittingAttributes: 方法默認調(diào)整Size屬性來適應(yīng) self-sizing Cell讨勤,所以重寫的時候需要先調(diào)用父類方法箭跳,再在返回的 UICollectionViewLayoutAttributes 對象上做你想要做的修改。
- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    ///此處調(diào)用父類所等到的attributes 和你最后得到的layoutAttributes的size完全一樣潭千,如果只是修改size完全沒必要重寫,如果layout沒有設(shè)置layout.estimatedItemSize谱姓,則attributes和layoutAttributes的size一樣
    UICollectionViewLayoutAttributes *attributes = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
    NSLog(@"%f====%f",layoutAttributes.size.width,layoutAttributes.size.height);
    NSLog(@"%f********%f",attributes.size.width,attributes.size.height);
   ///父類已經(jīng)調(diào)用
    CGSize size = [self.contentView systemLayoutSizeFittingSize:layoutAttributes.size];
    CGRect newFrame = layoutAttributes.frame;
    newFrame.size.width = size.width;
    layoutAttributes.frame = newFrame;
    NSLog(@"%f------%f",size.width,size.height);
    return attributes;
}

自定義UICollectionViewFlowLayout

先看一張圖,下面這張圖是使用estimatedItemSize來布局的一個collcetionview刨晴,現(xiàn)在我不想讓第一行兩個item之間的空隙太大屉来,這時候使用系統(tǒng)的layout向縮小是不可行的,因為就算我們設(shè)置minimumInteritemSpacing為10狈癞,上面已經(jīng)說了這個屬性只是item之間的最小間隔茄靠,item之間的實際間隔是可以大于10的,這時候如果用系統(tǒng)的layout就不是很好實現(xiàn)蝶桶,如果我們通過自定義layout則是很好實現(xiàn)的慨绳。

屏幕快照 2017-07-09 下午5.06.05.png

這張圖就可以實現(xiàn)我們上面的需求了,這就是通過自定義layout來實現(xiàn)的布局

屏幕快照 2017-07-09 下午5.09.28.png

自定義layout

自定義一個layout實際上很簡單真竖,你只需要創(chuàng)建一個繼承與UICollectionViewLayout的子類即可脐雪,然后重寫一部分方法,在這些方法里去實現(xiàn)你想要的布局方式恢共。

常用的方法

每次layout更新期間collectionview都會首先調(diào)用這個方法战秋,為將要開始的更新做準備,可以在此準備要使用的讨韭,你想要的layout的布局數(shù)組脂信。

///每次更新layout布局都會首先調(diào)用此方法
-(void)prepareLayout{
    NSLog(@"---------1");
    ///和init相似,必須call super的prepareLayout以保證初始化正確
    [super prepareLayout];
    ///1.首先被調(diào)用
    
    [self.attributesArray removeAllObjects];
    
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    
    for (int i =0; i<itemCount; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [self.attributesArray addObject:attributes];
        if (i==self.widthArray.count-1) {
            [self loadOldAttributes:attributes.frame];
        }
    }
}

collectionViewContentSize方法返回collectionview的ContentSize透硝,每次更新會調(diào)用兩次吉嚣,第一次是開始更新時調(diào)用,第二次是layoutAttributesForElementsInRect方法返回所有item的約束后調(diào)用蹬铺。

///返回collectionView的內(nèi)容的尺寸
-(CGSize)collectionViewContentSize{
    ///2.其次被調(diào)用(layoutAttributesForElementsInRect 調(diào)用后會在此調(diào)用此方法)
    NSLog(@"---%f------2",self.maxY);
    return CGSizeMake(self.collectionView.bounds.size.width, self.maxY);
}

返回rect中的所有的元素的布局屬性,返回的是包含UICollectionViewLayoutAttributes的NSArray,UICollectionViewLayoutAttributes可以是cell尝哆,追加視圖或裝飾視圖的信息,通過不同的UICollectionViewLayoutAttributes初始化方法可以得到不同類型的,初始的layout的外觀將由該方法返回的UICollectionViewLayoutAttributes來決定甜攀。

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    ///3.被調(diào)用
    NSLog(@"---------3");
    return self.attributesArray;
}

返回對應(yīng)于indexPath的位置的cell的布局屬性,返回指定indexPath的item的布局信息秋泄。子類必須重載該方法,該方法只能為cell提供布局信息琐馆,不能為補充視圖和裝飾視圖提供。

///返回對應(yīng)于indexPath的位置的cell的布局屬性,返回指定indexPath的item的布局信息恒序。子類必須重載該方法,該方法只能為cell提供布局信息瘦麸,不能為補充視圖和裝飾視圖提供。
-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath{
    UICollectionViewLayoutAttributes *attributs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    NSNumber *currentWidthNumber = self.widthArray[indexPath.row];
    CGFloat width = currentWidthNumber.floatValue;
    
    ///沒有換行所以超出部分不顯示(不寫下面的代碼也不會報錯歧胁,不知道為啥)
    if (width>[UIScreen mainScreen].bounds.size.width-(self.left+self.right)) {
        width = [UIScreen mainScreen].bounds.size.width - (self.left+self.right);
    }
    
    CGFloat height = 30;
    CGRect currentFrame = CGRectZero;

    if (1) {
        if (self.attributesArray.count!=0) {
            ///1.取出上一個item的attributes
            UICollectionViewLayoutAttributes *lastAttributs = [self.attributesArray lastObject];
            CGRect lastFrame = lastAttributs.frame;
            
            ///判斷當前item和上一個item是否在同一個row
            if (CGRectGetMaxX(lastAttributs.frame)+self.right==self.collectionView.bounds.size.width) {
                ///不在同一row
                currentFrame.origin.x = self.left;
                currentFrame.origin.y = CGRectGetMaxY(lastFrame) +self.top;
                currentFrame.size.width = width;
                currentFrame.size.height = height;
                attributs.frame = currentFrame;
                
            }else{
                ///上一個item的最大x值+當前item的寬度和左邊距
                CGFloat totleWidth = CGRectGetMaxX(lastFrame)+(self.between+width+self.right);
                ///判斷上一個item所在row的剩余寬度是否還夠顯示當前item
                if (totleWidth>=self.collectionView.bounds.size.width) {
                    ///不足以顯示當前item的寬度
                    
                    ///將和上一個item在同一個row的item的放在同一個數(shù)組
                    NSMutableArray *sameYArray = [NSMutableArray array];
                    for (UICollectionViewLayoutAttributes *subAttributs in self.attributesArray) {
                        if (subAttributs.frame.origin.y==lastFrame.origin.y) {
                            [sameYArray addObject:subAttributs];
                        }
                    }
                    
                    ///判斷出上一row還剩下多少寬度
                    CGFloat sameYWidth = 0.0;
                    for (UICollectionViewLayoutAttributes *sameYAttributs in sameYArray) {
                        sameYWidth += sameYAttributs.size.width;
                    }
                    sameYWidth = sameYWidth + (self.left+self.right+(sameYArray.count-1)*self.between);
                    ///上一個row所剩下的寬度
                    CGFloat sameYBetween = (self.collectionView.bounds.size.width-sameYWidth)/sameYArray.count;
                    
                    for (UICollectionViewLayoutAttributes *sameYAttributs in sameYArray) {
                        CGFloat sameAttributeWidth = sameYAttributs.size.width;
                        CGFloat sameAttributeHeight = sameYAttributs.size.height;
                        
                        CGRect sameYAttributsFrame = sameYAttributs.frame;
                        ///更新sameYAttributs寬度使之均衡顯示
                        sameAttributeWidth += sameYBetween;
                        sameYAttributs.size = CGSizeMake(sameAttributeWidth, sameAttributeHeight);
                        NSInteger index = [sameYArray indexOfObject:sameYAttributs];
                        
                        sameYAttributsFrame.origin.x += (sameYBetween*index);
                        sameYAttributsFrame.size.width = sameAttributeWidth;
                        sameYAttributs.frame = sameYAttributsFrame;
                    }
                    currentFrame.origin.x = self.left;
                    currentFrame.origin.y = CGRectGetMaxY(lastFrame)+self.top;
                    currentFrame.size.width = width;
                    currentFrame.size.height = height;
                    attributs.frame = currentFrame;
                    
                }else{
                    currentFrame.origin.x = CGRectGetMaxX(lastFrame)+self.between;
                    currentFrame.origin.y = lastFrame.origin.y;
                    currentFrame.size.width = width;
                    currentFrame.size.height = height;
                    attributs.frame = currentFrame;
                }
            }
        }else{
            currentFrame.origin.x = self.left;
            currentFrame.origin.y = self.top;
            currentFrame.size.width = width;
            currentFrame.size.height = height;
            attributs.frame = currentFrame;
        }
    }
    
//    attributs.size = CGSizeMake(width, 30);
    self.maxY = CGRectGetMaxY(attributs.frame)+10;
    
    NSLog(@"%f===%f===%f===%f",attributs.frame.origin.x,attributs.frame.origin.y,attributs.frame.size.width,attributs.frame.size.height);
    
    return attributs;
}

///返回對應(yīng)于indexPath的位置的追加視圖的布局屬性滋饲,如果沒有追加視圖可不重載
-(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
   return [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];
}

///返回對應(yīng)于indexPath的位置的裝飾視圖的布局屬性,如果沒有裝飾視圖可不重載
-(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)decorationViewKind atIndexPath:(NSIndexPath*)indexPath{
    return [super layoutAttributesForDecorationViewOfKind:decorationViewKind atIndexPath:indexPath];
}

///當邊界發(fā)生改變時喊巍,是否應(yīng)該刷新布局屠缭。如果YES則在邊界變化(一般是scroll到其他地方)時,將重新計算需要的布局信息崭参。
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return [super shouldInvalidateLayoutForBoundsChange:newBounds];
}

注意點: 另外呵曹,在需要更新layout時,需要給當前l(fā)ayout發(fā)送 -invalidateLayout何暮,該消息會立即返回奄喂,并且預(yù)約在下一個loop的時候刷新當前l(fā)ayout,這一點和UIView的setNeedsLayout方法十分類似海洼。在-invalidateLayout后的下一個collectionView的刷新loop中跨新,又會從prepareLayout開始,依次再調(diào)用-collectionViewContentSize和-layoutAttributesForElementsInRect來生成更新后的布局坏逢。

全部代碼

#import "DWCollectionViewLayout.h"


#define DWScreenH = [UIScreen mainScreen].bounds.size.height
#define DWScreenW = [UIScreen mainScreen].bounds.size.width


@interface DWCollectionViewLayout ()

@property (nonatomic,strong) NSMutableArray *attributesArray;

@property (nonatomic,assign) CGFloat maxY;

@property (nonatomic,assign) CGFloat left;

@property (nonatomic,assign) CGFloat right;

@property (nonatomic,assign) CGFloat top;

@property (nonatomic,assign) CGFloat between;

@end




@implementation DWCollectionViewLayout


-(instancetype)initWithArray:(NSMutableArray*)widthArray edgeInsets:(UIEdgeInsets)insets{
    if (self = [super init]) {
        self.widthArray = widthArray;
        NSLog(@"==***==%p",self.widthArray);

        self.left = insets.left;
        self.right = insets.right;
        self.top = insets.top;
        self.between = insets.bottom;
    }
    return self;
}


/**
 *另外需要了解的是域帐,在初始化一個UICollectionViewLayout實例后,會有一系列準備方法被自動調(diào)用词疼,以保證layout實例的正確。
 
 *首先帘腹,將被調(diào)用贰盗,默認下該方法什么沒做,但是在自己的子類實現(xiàn)中阳欲,一般在該方法中設(shè)定一些必要的layout的結(jié)構(gòu)和初始需要的參數(shù)等舵盈。
 
 */

-(void)prepareLayout{
    
    NSLog(@"---------1");
    
    ///和init相似,必須call super的prepareLayout以保證初始化正確
    [super prepareLayout];
    ///1.首先被調(diào)用
    
    [self.attributesArray removeAllObjects];
    
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    
    for (int i =0; i<itemCount; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [self.attributesArray addObject:attributes];
        if (i==self.widthArray.count-1) {
            [self loadOldAttributes:attributes.frame];
        }
    }
}


///返回collectionView的內(nèi)容的尺寸
-(CGSize)collectionViewContentSize{
    ///2.其次被調(diào)用(layoutAttributesForElementsInRect 調(diào)用后會在此調(diào)用此方法)
    NSLog(@"---%f------2",self.maxY);
    return CGSizeMake(self.collectionView.bounds.size.width, self.maxY);
}

///返回rect中的所有的元素的布局屬性,返回的是包含UICollectionViewLayoutAttributes的NSArray
///UICollectionViewLayoutAttributes可以是cell球化,追加視圖或裝飾視圖的信息秽晚,通過不同的UICollectionViewLayoutAttributes初始化方法可以得到不同類型的
///初始的layout的外觀將由該方法返回的UICollectionViewLayoutAttributes來決定。

///rect 為collectionview 的rect筒愚,(高度超出當前屏幕的高度后赴蝇,rect的height會翻倍)
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    ///3.被調(diào)用
    NSLog(@"---------3");

    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    
    for (UICollectionViewLayoutAttributes *attributes in array) {
        if (attributes.representedElementCategory == UICollectionElementCategoryCell) {
        }
    }
    return self.attributesArray;
}


///返回對應(yīng)于indexPath的位置的cell的布局屬性,返回指定indexPath的item的布局信息。子類必須重載該方法,該方法只能為cell提供布局信息巢掺,不能為補充視圖和裝飾視圖提供句伶。
-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath{
    UICollectionViewLayoutAttributes *attributs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    NSNumber *currentWidthNumber = self.widthArray[indexPath.row];
    CGFloat width = currentWidthNumber.floatValue;
    
    ///沒有換行所以超出部分不顯示(不寫下面的代碼也不會報錯劲蜻,不知道為啥)
    if (width>[UIScreen mainScreen].bounds.size.width-(self.left+self.right)) {
        width = [UIScreen mainScreen].bounds.size.width - (self.left+self.right);
    }
    
    CGFloat height = 30;
    CGRect currentFrame = CGRectZero;

    if (1) {
        if (self.attributesArray.count!=0) {
            ///1.取出上一個item的attributes
            UICollectionViewLayoutAttributes *lastAttributs = [self.attributesArray lastObject];
            CGRect lastFrame = lastAttributs.frame;
            
            ///判斷當前item和上一個item是否在同一個row
            if (CGRectGetMaxX(lastAttributs.frame)+self.right==self.collectionView.bounds.size.width) {
                ///不在同一row
                currentFrame.origin.x = self.left;
                currentFrame.origin.y = CGRectGetMaxY(lastFrame) +self.top;
                currentFrame.size.width = width;
                currentFrame.size.height = height;
                attributs.frame = currentFrame;
                
            }else{
                ///上一個item的最大x值+當前item的寬度和左邊距
                CGFloat totleWidth = CGRectGetMaxX(lastFrame)+(self.between+width+self.right);
                ///判斷上一個item所在row的剩余寬度是否還夠顯示當前item
                if (totleWidth>=self.collectionView.bounds.size.width) {
                    ///不足以顯示當前item的寬度
                    
                    ///將和上一個item在同一個row的item的放在同一個數(shù)組
                    NSMutableArray *sameYArray = [NSMutableArray array];
                    for (UICollectionViewLayoutAttributes *subAttributs in self.attributesArray) {
                        if (subAttributs.frame.origin.y==lastFrame.origin.y) {
                            [sameYArray addObject:subAttributs];
                        }
                    }
                    
                    ///判斷出上一row還剩下多少寬度
                    CGFloat sameYWidth = 0.0;
                    for (UICollectionViewLayoutAttributes *sameYAttributs in sameYArray) {
                        sameYWidth += sameYAttributs.size.width;
                    }
                    sameYWidth = sameYWidth + (self.left+self.right+(sameYArray.count-1)*self.between);
                    ///上一個row所剩下的寬度
                    CGFloat sameYBetween = (self.collectionView.bounds.size.width-sameYWidth)/sameYArray.count;
                    
                    for (UICollectionViewLayoutAttributes *sameYAttributs in sameYArray) {
                        CGFloat sameAttributeWidth = sameYAttributs.size.width;
                        CGFloat sameAttributeHeight = sameYAttributs.size.height;
                        
                        CGRect sameYAttributsFrame = sameYAttributs.frame;
                        ///更新sameYAttributs寬度使之均衡顯示
                        sameAttributeWidth += sameYBetween;
                        sameYAttributs.size = CGSizeMake(sameAttributeWidth, sameAttributeHeight);
                        NSInteger index = [sameYArray indexOfObject:sameYAttributs];
                        
                        sameYAttributsFrame.origin.x += (sameYBetween*index);
                        sameYAttributsFrame.size.width = sameAttributeWidth;
                        sameYAttributs.frame = sameYAttributsFrame;
                    }
                    currentFrame.origin.x = self.left;
                    currentFrame.origin.y = CGRectGetMaxY(lastFrame)+self.top;
                    currentFrame.size.width = width;
                    currentFrame.size.height = height;
                    attributs.frame = currentFrame;
                    
                }else{
                    currentFrame.origin.x = CGRectGetMaxX(lastFrame)+self.between;
                    currentFrame.origin.y = lastFrame.origin.y;
                    currentFrame.size.width = width;
                    currentFrame.size.height = height;
                    attributs.frame = currentFrame;
                }
            }
        }else{
            currentFrame.origin.x = self.left;
            currentFrame.origin.y = self.top;
            currentFrame.size.width = width;
            currentFrame.size.height = height;
            attributs.frame = currentFrame;
        }
    }
    
//    attributs.size = CGSizeMake(width, 30);
    self.maxY = CGRectGetMaxY(attributs.frame)+10;
    
    NSLog(@"%f===%f===%f===%f",attributs.frame.origin.x,attributs.frame.origin.y,attributs.frame.size.width,attributs.frame.size.height);
    
    return attributs;
}

///返回對應(yīng)于indexPath的位置的追加視圖的布局屬性,如果沒有追加視圖可不重載
-(UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
   return [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath];
}

///返回對應(yīng)于indexPath的位置的裝飾視圖的布局屬性考余,如果沒有裝飾視圖可不重載
-(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)decorationViewKind atIndexPath:(NSIndexPath*)indexPath{
    return [super layoutAttributesForDecorationViewOfKind:decorationViewKind atIndexPath:indexPath];
}

///當邊界發(fā)生改變時先嬉,是否應(yīng)該刷新布局。如果YES則在邊界變化(一般是scroll到其他地方)時楚堤,將重新計算需要的布局信息疫蔓。
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return [super shouldInvalidateLayoutForBoundsChange:newBounds];
}


/**
  另外,在需要更新layout時身冬,需要給當前l(fā)ayout發(fā)送 -invalidateLayout衅胀,該消息會立即返回,并且預(yù)約在下一個loop的時候刷新當前l(fā)ayout吏恭,這一點和UIView的setNeedsLayout方法十分類似拗小。在-invalidateLayout后的下一個collectionView的刷新loop中,又會從prepareLayout開始樱哼,依次再調(diào)用-collectionViewContentSize和-layoutAttributesForElementsInRect來生成更新后的布局哀九。
 */



-(void)loadOldAttributes:(CGRect)lastFrame{
    ///將和上一個item在同一個row的item的放在同一個數(shù)組
    NSMutableArray *sameYArray = [NSMutableArray array];
    for (UICollectionViewLayoutAttributes *subAttributs in self.attributesArray) {
        if (subAttributs.frame.origin.y==lastFrame.origin.y) {
            [sameYArray addObject:subAttributs];
        }
    }
    
    ///判斷出上一row還剩下多少寬度
    CGFloat sameYWidth = 0.0;
    for (UICollectionViewLayoutAttributes *sameYAttributs in sameYArray) {
        sameYWidth += sameYAttributs.size.width;
    }
    sameYWidth = sameYWidth + (self.left+self.right+(sameYArray.count-1)*self.between);
    ///上一個row所剩下的寬度
    CGFloat sameYBetween = (self.collectionView.bounds.size.width-sameYWidth)/sameYArray.count;
    
    for (UICollectionViewLayoutAttributes *sameYAttributs in sameYArray) {
        CGFloat sameAttributeWidth = sameYAttributs.size.width;
        CGFloat sameAttributeHeight = sameYAttributs.size.height;
        
        CGRect sameYAttributsFrame = sameYAttributs.frame;
        ///更新sameYAttributs寬度使之均衡顯示
        sameAttributeWidth += sameYBetween;
        sameYAttributs.size = CGSizeMake(sameAttributeWidth, sameAttributeHeight);
        NSInteger index = [sameYArray indexOfObject:sameYAttributs];
        
        sameYAttributsFrame.origin.x += (sameYBetween*index);
        sameYAttributsFrame.size.width = sameAttributeWidth;
        sameYAttributs.frame = sameYAttributsFrame;
    }
}

-(NSMutableArray*)attributesArray{
    if (!_attributesArray) {
        _attributesArray = [NSMutableArray array];
    }
    return _attributesArray;
}

@end

區(qū)頭懸浮

#import "DWReusableLayout.h"

@interface DWReusableLayout ()

@property (nonatomic,assign) CGFloat naviHeight;

@end

@implementation DWReusableLayout


-(instancetype)init
{
    self = [super init];
    if (self)
    {
        self.naviHeight = 0.0;
    }
    return self;
}
/*
 
 // 作用:返回指定區(qū)域的cell布局對象
 // 什么時候調(diào)用:指定新的區(qū)域的時候調(diào)用
 (<__kindof UICollectionViewLayoutAttributes *>   iOS9之后的泛型 )
 - (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
 */

/** iOS9 之前的寫法 作用第24行代碼有寫*/
//UICollectionViewLayoutAttributes:我稱它為collectionView中的item(包括cell和header、footer這些)的《結(jié)構(gòu)信息》
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect
{
    
    //截取到父類所返回的數(shù)組(里面放的是當前屏幕所能展示的item的結(jié)構(gòu)信息)搅幅,并轉(zhuǎn)化成不可變數(shù)組
    NSMutableArray *superArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
    
    //創(chuàng)建存索引的數(shù)組阅束,無符號(正整數(shù)),無序(不能通過下標取值)茄唐,不可重復(fù)(重復(fù)的話會自動過濾)
    NSMutableIndexSet *noneHeaderSections = [NSMutableIndexSet indexSet];
    //遍歷superArray息裸,得到一個當前屏幕中所有的section數(shù)組
    for (UICollectionViewLayoutAttributes *attributes in superArray)
    {
        //如果當前的元素分類是一個cell,將cell所在的分區(qū)section加入數(shù)組沪编,重復(fù)的話會自動過濾
        if (attributes.representedElementCategory == UICollectionElementCategoryCell)
        {
            [noneHeaderSections addIndex:attributes.indexPath.section];
        }
    }
    
    //遍歷superArray呼盆,將當前屏幕中擁有的header的section從數(shù)組中移除,得到一個當前屏幕中沒有header的section數(shù)組
    //正常情況下蚁廓,隨著手指往上移访圃,header脫離屏幕會被系統(tǒng)回收而cell尚在,也會觸發(fā)該方法
    for (UICollectionViewLayoutAttributes *attributes in superArray)
    {
        //如果當前的元素是一個header相嵌,將header所在的section從數(shù)組中移除
        if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
        {
            [noneHeaderSections removeIndex:attributes.indexPath.section];
        }
    }
    
    //遍歷當前屏幕中沒有header的section數(shù)組
    [noneHeaderSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
        
        //取到當前section中第一個item的indexPath
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
        //獲取當前section在正常情況下已經(jīng)離開屏幕的header結(jié)構(gòu)信息
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
        
        //如果當前分區(qū)確實有因為離開屏幕而被系統(tǒng)回收的header
        if (attributes)
        {
            //將該header結(jié)構(gòu)信息重新加入到superArray中去
            [superArray addObject:attributes];
        }
    }];
    
    //遍歷superArray腿时,改變header結(jié)構(gòu)信息中的參數(shù),使它可以在當前section還沒完全離開屏幕的時候一直顯示
    for (UICollectionViewLayoutAttributes *attributes in superArray) {
        
        //如果當前item是header
        if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
        {
            //得到當前header所在分區(qū)的cell的數(shù)量
            NSInteger numberOfItemsInSection = [self.collectionView numberOfItemsInSection:attributes.indexPath.section];
            //得到第一個item的indexPath
            NSIndexPath *firstItemIndexPath = [NSIndexPath indexPathForItem:0 inSection:attributes.indexPath.section];
            //得到最后一個item的indexPath
            NSIndexPath *lastItemIndexPath = [NSIndexPath indexPathForItem:MAX(0, numberOfItemsInSection-1) inSection:attributes.indexPath.section];
            //得到第一個item和最后一個item的結(jié)構(gòu)信息
            UICollectionViewLayoutAttributes *firstItemAttributes, *lastItemAttributes;
            if (numberOfItemsInSection>0)
            {
                //cell有值饭宾,則獲取第一個cell和最后一個cell的結(jié)構(gòu)信息
                firstItemAttributes = [self layoutAttributesForItemAtIndexPath:firstItemIndexPath];
                lastItemAttributes = [self layoutAttributesForItemAtIndexPath:lastItemIndexPath];
            }else
            {
                //cell沒值,就新建一個UICollectionViewLayoutAttributes
                firstItemAttributes = [UICollectionViewLayoutAttributes new];
                //然后模擬出在當前分區(qū)中的唯一一個cell批糟,cell在header的下面,高度為0看铆,還與header隔著可能存在的sectionInset的top
                CGFloat y = CGRectGetMaxY(attributes.frame)+self.sectionInset.top;
                firstItemAttributes.frame = CGRectMake(0, y, 0, 0);
                //因為只有一個cell徽鼎,所以最后一個cell等于第一個cell
                lastItemAttributes = firstItemAttributes;
            }
            
            //獲取當前header的frame
            CGRect rect = attributes.frame;
            
            //當前的滑動距離 + 因為導(dǎo)航欄產(chǎn)生的偏移量,默認為64(如果app需求不同,需自己設(shè)置)
            CGFloat offset = self.collectionView.contentOffset.y + _naviHeight;
            //第一個cell的y值 - 當前header的高度 - 可能存在的sectionInset的top
            
            ///firstItemAttributes.frame.origin.y-self.sectionInset.top = CGRectGetMaxY(attributes)(即緊貼header的最大Y值 = header.frame.origin.y+header.bounds.size.height)
            ///firstItemAttributes.frame.origin.y-self.sectionInset.top = header.frame.origin.y+header.bounds.size.height
            
            ///header.frame.origin.y = firstItemAttributes.frame.origin.y-self.sectionInset.top-header.bounds.size.height
            CGFloat headerY = firstItemAttributes.frame.origin.y - rect.size.height - self.sectionInset.top;
            
            //哪個大取哪個纬傲,保證header懸停
            //針對當前header基本上都是offset更加大满败,針對下一個header則會是headerY大,各自處理
            CGFloat maxY = MAX(offset,headerY);
            
            //最后一個cell的y值 + 最后一個cell的高度 + 可能存在的sectionInset的bottom - 當前header的高度
            //當當前section的footer或者下一個section的header接觸到當前header的底部叹括,計算出的headerMissingY即為有效值
            CGFloat headerMissingY = CGRectGetMaxY(lastItemAttributes.frame) + self.sectionInset.bottom - rect.size.height;
            
            //給rect的y賦新值算墨,因為在最后消失的臨界點要跟誰消失,所以取小
            ///兩個區(qū)頭沒有接觸之前汁雷,offset<headerMissingY净嘀,所以rect.origin.y==偏移量,接觸時offset=headerMissingY侠讯,接觸后挖藏,offset>headerMissingY
            ///所以接觸后最小值就是headerMissingY(即上一個區(qū)的最大Y值-rect.size.height)
            rect.origin.y = MIN(maxY,headerMissingY);
            
            NSLog(@"%f-----%f----%f---%f",offset,headerY,headerMissingY,rect.origin.y);
            
            //給header的結(jié)構(gòu)信息的frame重新賦值
            attributes.frame = rect;
            
            //如果按照正常情況下,header離開屏幕被系統(tǒng)回收,而header的層次關(guān)系又與cell相等厢漩,如果不去理會膜眠,會出現(xiàn)cell在header上面的情況
            //通過打印可以知道cell的層次關(guān)系zIndex數(shù)值為0,我們可以將header的zIndex設(shè)置成1溜嗜,如果不放心宵膨,也可以將它設(shè)置成非常大,這里隨便填了個7
            attributes.zIndex = 7;
        }
    }
    //轉(zhuǎn)換回不可變數(shù)組炸宵,并返回
    return [superArray copy];
    
}

//return YES;表示一旦滑動就實時調(diào)用上面這個layoutAttributesForElementsInRect:方法
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound
{
    return YES;
}

@end

屏幕快照 2017-07-09 下午5.40.17.png

瀑布流

#import "DWWateFallLayout.h"

@interface DWWateFallLayout ()

@property (nonatomic,strong) NSMutableArray *attributesArray;

@property (nonatomic,strong) NSArray<NSNumber*> *itemHeightArray;

@property (nonatomic,strong) NSMutableArray<UICollectionViewLayoutAttributes*> *itemArray;

@end

@implementation DWWateFallLayout

-(instancetype)initWithHeightArray:(NSArray*)heightArray{
    if (self = [super init]) {
        self.itemHeightArray = heightArray;
    }
    return self;
}


-(void)prepareLayout{
        
    ///和init相似辟躏,必須call super的prepareLayout以保證初始化正確
    [super prepareLayout];
    ///1.首先被調(diào)用
    
    [self.attributesArray removeAllObjects];
    [self.itemArray removeAllObjects];
        
    ///獲取當前collectionView對應(yīng)區(qū)的item
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    
    for (int i =0; i<count; i++) {
       UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
        [self.attributesArray addObject:attributes];
    }
    
}

///返回collectionView的內(nèi)容的尺寸
-(CGSize)collectionViewContentSize{
    ///2.其次被調(diào)用(layoutAttributesForElementsInRect 調(diào)用后會在此調(diào)用此方法)
    
    
    CGFloat maxContentHeight = CGRectGetMaxY([self.itemArray firstObject].frame);
    
    for (UICollectionViewLayoutAttributes *attributes in self.itemArray) {
        if (maxContentHeight<CGRectGetMaxY(attributes.frame)) {
            maxContentHeight = CGRectGetMaxY(attributes.frame);
        }
    }
    return CGSizeMake(self.collectionView.bounds.size.width, maxContentHeight);
}


///返回rect中的所有的元素的布局屬性,返回的是包含UICollectionViewLayoutAttributes的NSArray
///UICollectionViewLayoutAttributes可以是cell,追加視圖或裝飾視圖的信息土全,通過不同的UICollectionViewLayoutAttributes初始化方法可以得到不同類型的
///初始的layout的外觀將由該方法返回的UICollectionViewLayoutAttributes來決定捎琐。

///rect 為collectionview 的rect,(高度超出collectionview高度后裹匙,rect的height會翻倍)
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    ///3.被調(diào)用
    return self.attributesArray;
}

///返回對應(yīng)于indexPath的位置的cell的布局屬性,返回指定indexPath的item的布局信息瑞凑。子類必須重載該方法,該方法只能為cell提供布局信息,不能為補充視圖和裝飾視圖提供概页。
-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath{
    UICollectionViewLayoutAttributes *attributs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    ///item的寬度籽御,根據(jù)左右間距和中間間距算出item寬度
    CGFloat itemWidth = (self.collectionView.bounds.size.width - (10+10+10+10))/3.0;
    ///item的高度
    CGFloat itemHeight = self.itemHeightArray[indexPath.row].floatValue;
    
    if (self.itemArray.count<3) {
        [self.itemArray addObject:attributs];
        CGRect itemFrame = CGRectMake(10+(itemWidth+10)*(self.itemArray.count-1), 10, itemWidth, itemHeight);
        attributs.frame = itemFrame;
    }else{
        UICollectionViewLayoutAttributes *fristAttri = [self.itemArray firstObject];
        CGFloat minY = CGRectGetMaxY(fristAttri.frame);
        CGFloat Y = minY;
        NSInteger index=0;
        CGRect itemFrame = CGRectMake(fristAttri.frame.origin.x,CGRectGetMaxY(fristAttri.frame)+10, itemWidth, itemHeight);
        for (UICollectionViewLayoutAttributes *attri in self.itemArray) {
            if (minY>CGRectGetMaxY(attri.frame)) {
                minY = CGRectGetMaxY(attri.frame);
                Y = minY;
                itemFrame = CGRectMake(attri.frame.origin.x,Y+10, itemWidth, itemHeight);
                NSInteger currentIndex = [self.itemArray indexOfObject:attri];
                index = currentIndex;
            }
        }
        attributs.frame = itemFrame;
        [self.itemArray replaceObjectAtIndex:index withObject:attributs];
    }
    
    return attributs;
}

-(NSMutableArray*)attributesArray{
    if (!_attributesArray) {
        _attributesArray = [NSMutableArray array];
    }
    return _attributesArray;
}

-(NSMutableArray*)itemArray{
    if (!_itemArray) {
        _itemArray = [NSMutableArray array];
    }
    return _itemArray;
}

@end

屏幕快照 2017-07-09 下午5.40.02.png

這些只是本人在使用過程中,了解到的一些知識點僅供參考绰沥,如有錯誤歡迎指正篱蝇。其他的就不多說了贺待,具體的實現(xiàn)過程代碼里都有詳細的注釋徽曲,還有一些注意點懶得寫了都在代碼里面有興趣的可以看看。

demo地址:https://github.com/dachongdouniwan/DWCollectionView

寫的不好大家隨便看看吧麸塞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(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)自己被綠了。 大學時的朋友給我發(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)容