iOS--瀑布流的實(shí)現(xiàn)

最近一直在做項(xiàng)目,沒什么時間發(fā)博客.現(xiàn)在有空了,整理一下這段時間項(xiàng)目里的一些實(shí)用技術(shù).如果有問題歡迎隨時找我交流.

iOS瀑布流,實(shí)現(xiàn)的是對于不同尺寸控件的合理布局

舉個例子:(ONE一個的往期列表)
ONE一個

通過collectionView利用自定義的layout進(jìn)行布局,cell進(jìn)行自適應(yīng).我個人覺得這個效果特別適合一個這個APP.

下面來看一下具體的代碼實(shí)現(xiàn):

首先,看一下數(shù)據(jù)內(nèi)容:(這里我找了一個本地的json文件當(dāng)做數(shù)據(jù)源)
     {
         "thumbURL":"http://amuse.nen.com.cn/imagelist/11/21/9as70n3ir61b.jpg",
         "width": 482,
         "height": 480
     }

代碼內(nèi)容:

MyLayOut.h
    #import <UIKit/UIKit.h>
    
    @protocol MyLayOutDelegate <NSObject>
    
    /**
     *  獲取item的高度
     *
     *  @param indexPath 下標(biāo)
     *
     *  @return item高度
     */
    - (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath;
    
    @end
    
    @interface MyLayOut : UICollectionViewLayout
    
    /**
     *  單元格尺寸
     */
    @property (nonatomic, assign) CGSize itemSize;
    
    /**
     *  列數(shù)
     */
    @property (nonatomic, assign) NSInteger numberOfColumns;
    
    /**
     *  內(nèi)邊距
     */
    @property (nonatomic, assign) UIEdgeInsets sectionInSet;
    
    /**
     *  item間隔
     */
    @property (nonatomic, assign) CGFloat ItemSpacing;
    
    /**
     *  代理人屬性
     */
    @property (nonatomic, assign) id<MyLayOutDelegate>delegate;

    @end
MyLayOut.m
    #import "MyLayOut.h"
    
    @interface MyLayOut ()
    
    /**
     *  列高
     */
    @property (nonatomic, strong) NSMutableArray *columnsHeights;
    
    /**
     *  item的數(shù)量
     */
    @property (nonatomic, assign) NSInteger numberOfItems;
    
    /**
     *  存放每個item的位置信息的數(shù)組
     */
    @property (nonatomic, strong) NSMutableArray *itemAttributes;
    
    /**
     *  臨時存儲當(dāng)前item的x值
     */
    @property (nonatomic, assign) CGFloat item_X;
    
    /**
     *  臨時存儲當(dāng)前item的Y值
     */
    @property (nonatomic, assign) CGFloat item_Y;
    
    /**
     *  最矮列下標(biāo)
     */
    
    @property (nonatomic, assign) NSInteger shortestIndex;
    
    @end
    
    @implementation MyLayOut
    
    #pragma mark -------------------- 懶加載
    - (NSMutableArray *)columnsHeights
    {
        if (!_columnsHeights)
        {
            self.columnsHeights = [NSMutableArray array];
        }
        return _columnsHeights;
    }
    
    - (NSMutableArray *)itemAttributes
    {
        if (!_itemAttributes)
        {
            self.itemAttributes = [NSMutableArray array];
        }
        return _itemAttributes;
    }
    
    #pragma mark -- 獲取最矮列的下標(biāo)
    - (NSInteger)getShortestColumnIndex
    {
        //最矮列下標(biāo)
        NSInteger shortestIndex = 0;
        
        //column高度
        CGFloat shortestHeight = MAXFLOAT;
        
        //遍歷高度數(shù)組獲得最矮列下標(biāo)
        for (NSInteger i = 0; i < self.numberOfColumns; i ++)
        {
            CGFloat currentHeight = [[self.columnsHeights objectAtIndex:i] floatValue];
            if (currentHeight < shortestHeight)
            {
                shortestHeight = currentHeight;
                shortestIndex = i;
            }
        }
        return shortestIndex;
    }
    
    #pragma mark -- 獲取最高列的下標(biāo)
    - (NSInteger)getHighestColumnIndex
    {
        //最高列下標(biāo)
        NSInteger highestIndex = 0;
        
        //column高度
        CGFloat highestHeight = 0;
        
        //遍歷高度數(shù)組獲得最高列下標(biāo)
        for (NSInteger i = 0; i < self.numberOfColumns; i ++)
        {
            CGFloat currentHeight = [[self.columnsHeights objectAtIndex:i] floatValue];
            if (currentHeight > highestHeight)
            {
                highestHeight = currentHeight;
                highestIndex = i;
            }
        }
        return highestIndex;
    }
    
    #pragma mark -- 添加頂部內(nèi)邊距的值
    - (void)addTopValueForColumns
    {
        for (NSInteger i = 0; i < self.numberOfColumns; i ++)
        {
            self.columnsHeights[i] = @(self.sectionInSet.top);
        }
    }
    
    #pragma mark -- 計(jì)算每個item的X和Y
    - (void)getOriginInShortestColumn
    {
        //獲取最矮列下標(biāo)
        self.shortestIndex = [self getShortestColumnIndex];
        
        //獲取最矮列的高度
        CGFloat shortestHeight = [[self.columnsHeights objectAtIndex:self.shortestIndex] floatValue];
        
        //設(shè)置item的X
        self.item_X = self.sectionInSet.left + (self.itemSize.width + self.ItemSpacing) * self.shortestIndex;
        
        //設(shè)置item的Y
        self.item_Y = shortestHeight + self.ItemSpacing;
    }
    
    #pragma mark -- 計(jì)算width和height --> 生成frame
    - (void)setFrame:(NSIndexPath *)indexPath
    {
        UICollectionViewLayoutAttributes *layOutAttribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        
        //存放item的高度
        CGFloat itemHeight = 0;
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(heightForItemAtIndexPath:)])
        {
            itemHeight = [self.delegate heightForItemAtIndexPath:indexPath];
        }
        
        layOutAttribute.frame = CGRectMake(_item_X, _item_Y, self.itemSize.width, itemHeight);
        
        //將位置信息加入數(shù)組
        [self.itemAttributes addObject:layOutAttribute];
        
        //更新當(dāng)前列的高度
        self.columnsHeights[_shortestIndex] = @(self.item_Y + itemHeight);
    }
    
    #pragma mark -- 重寫父類布局方法
    - (void)prepareLayout
    {
        [super prepareLayout];
        
        //為高度數(shù)組添加上邊距
        [self addTopValueForColumns];
        
        //獲取item個數(shù)
        self.numberOfItems = [self.collectionView numberOfItemsInSection:0];
        
        //循環(huán)布局
        for (NSInteger i = 0; i < self.numberOfItems; i ++)
        {
            //計(jì)算item的X和Y
            [self getOriginInShortestColumn];
            
            //生成indexPath
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
            
            //生成frame
            [self setFrame:indexPath];
        }
    }
    
    #pragma mark -- 獲取contentView尺寸
    - (CGSize)collectionViewContentSize
    {
        //獲取最高列下標(biāo)
        NSInteger highestIndex = [self getHighestColumnIndex];
        
        //獲取最高列高度
        CGFloat highestHeight = [[self.columnsHeights objectAtIndex:highestIndex] floatValue];
        
        //構(gòu)造contentView的size
        CGSize size = self.collectionView.frame.size;
        
        //修改高度
        size.height = highestHeight + self.sectionInSet.bottom;
        
        //返回size
        return size;
    }
    
    #pragma mark -- 返回位置信息數(shù)組
    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
    {
        return self.itemAttributes;
    }

@end
ViewController.m
    #import "ViewController.h"
    #import "Model.h"
    #import "MyLayOut.h"
    #import "ImageCell.h"
    #import "UIImageView+WebCache.h"
    
    @interface ViewController ()<MyLayOutDelegate, UICollectionViewDelegate, UICollectionViewDataSource>
    
    /**
     *  數(shù)據(jù)源數(shù)組
     */
    @property (nonatomic, strong) NSMutableArray *dataSource;
    
    /**
     *  集合視圖
     */
    @property (nonatomic, strong) UICollectionView *collectionView;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        [self getData];
        [self createCollectionView];
    }
    
    #pragma mark -------------------- 請求數(shù)據(jù)
    - (void)getData
    {
        self.dataSource = [NSMutableArray array];
        
        NSData *data = [NSData dataWithContentsOfFile:@"/Users/dllo/Desktop/簡書/MyFlowLayOut/MyFlowLayOut/Data.json"];
        NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
        
        for (NSDictionary *dic in array)
        {
            Model *model = [[Model alloc] init];
            [model setValuesForKeysWithDictionary:dic];
            [self.dataSource addObject:model];
        }
    }
    
    #pragma mark -------------------- 創(chuàng)建collectionView
    - (void)createCollectionView
    {
        MyLayOut *layOut = [[MyLayOut alloc] init];
        layOut.delegate = self;
        layOut.itemSize = CGSizeMake((self.view.frame.size.width - 40) / 3.0, (self.view.frame.size.width - 40) / 3.0);
        layOut.ItemSpacing = 10;
        layOut.sectionInSet = UIEdgeInsetsMake(10, 10, 10, 10);
        layOut.numberOfColumns = 3;
        
        self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layOut];
        self.collectionView.delegate = self;
        self.collectionView.dataSource = self;
        self.collectionView.backgroundColor = [UIColor colorWithRed:0.52 green:0.74 blue:0.96 alpha:1.00];
        [self.view addSubview:_collectionView];
        
        //注冊cell:
        [self.collectionView registerClass:[ImageCell class] forCellWithReuseIdentifier:@"imageCell"];
    }
    
    #pragma mark -------------------- 實(shí)現(xiàn)自定義協(xié)議方法
    - (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        CGFloat width = ([UIScreen mainScreen].bounds.size.width - 40) / 3.0;
        
        Model *model = [self.dataSource objectAtIndex:indexPath.row];
        CGFloat height = (model.height / model.width) * width;
        return height;
    }
    
    #pragma mark -------------------- 實(shí)現(xiàn)collectionView協(xié)議方法
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
        return self.dataSource.count;
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"imageCell" forIndexPath:indexPath];
        cell.backgroundColor = [UIColor lightGrayColor];
        cell.layer.cornerRadius = 5;
        cell.photoImageView.layer.cornerRadius = 5;
        cell.photoImageView.clipsToBounds = YES;
        cell.photoImageView.contentMode = UIViewContentModeScaleAspectFit;
        [cell.photoImageView sd_setImageWithURL:[NSURL URLWithString:[[self.dataSource objectAtIndex:indexPath.row] thumbURL]]];
        return cell;
    }
    
    - (UIStatusBarStyle)preferredStatusBarStyle
    {
        return UIStatusBarStyleLightContent;
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
}

@end

實(shí)現(xiàn)效果如下:

實(shí)現(xiàn)效果

在寫的過程中也遇到了很多問題,因?yàn)閷τ?code>layout的實(shí)現(xiàn)我也不是很熟悉,還需要進(jìn)一步調(diào)研,希望大家都能共同進(jìn)步!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谎碍,隨后出現(xiàn)的幾起案子莉兰,更是在濱河造成了極大的恐慌却邓,老刑警劉巖谷市,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柄驻,死亡現(xiàn)場離奇詭異胎挎,居然都是意外死亡胁出,警方通過查閱死者的電腦和手機(jī)型型,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來全蝶,“玉大人闹蒜,你說我怎么就攤上這事寺枉。” “怎么了绷落?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵姥闪,是天一觀的道長。 經(jīng)常有香客問我砌烁,道長筐喳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任函喉,我火速辦了婚禮避归,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘函似。我一直安慰自己槐脏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布撇寞。 她就那樣靜靜地躺著,像睡著了一般堂氯。 火紅的嫁衣襯著肌膚如雪蔑担。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天咽白,我揣著相機(jī)與錄音啤握,去河邊找鬼。 笑死晶框,一個胖子當(dāng)著我的面吹牛排抬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播授段,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼蹲蒲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了侵贵?” 一聲冷哼從身側(cè)響起届搁,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窍育,沒想到半個月后卡睦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漱抓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年表锻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞬逊。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡显歧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出码耐,到底是詐尸還是另有隱情,我是刑警寧澤骚腥,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布束铭,位于F島的核電站带猴,受9級特大地震影響懈万,放射性物質(zhì)發(fā)生泄漏拴清。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涕侈。 院中可真熱鬧沪停,春花似錦、人聲如沸裳涛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽端三。三九已至舷礼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間技肩,已是汗流浹背且轨。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虚婿,地道東北人旋奢。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像然痊,于是被迫代替她去往敵國和親至朗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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

  • 為何我會對瀑布流為這么上進(jìn)剧浸,非要將瀑布流來一次大清洗锹引,非逼得自己將它徹底弄清楚矗钟。原因其實(shí)也很簡單:之前剛出來找工作...
    smooth_lgh閱讀 4,723評論 1 5
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件嫌变、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,033評論 4 62
  • 三種方法實(shí)現(xiàn) 一吨艇、scrollView做墊子,上面添加多個tableView(不可取)腾啥; 效率低下东涡,cell不能循...
    SadMine閱讀 752評論 0 0
  • 天空很藍(lán),沒有一片云 地里的油菜倘待,開花了疮跑! 蜜蜂爭著采蜜,吐出了各種口味. ...
    丹珍旺唐閱讀 213評論 0 0
  • 波士頓矩陣其實(shí)就是在綜合分析自己的優(yōu)勢劣勢后凸舵,進(jìn)行有針對性的投放精力祖娘,有的放矢,讓自己在競爭發(fā)展中更具優(yōu)勢啊奄。
    423429d90f38閱讀 184評論 0 0