Swift瀑布流展示/切換簡書列表數據

上篇文章介紹了HTML解析數據存入模型,今天我們的任務是將把之前拿到的數據可視化展示出來。因為組數據的的標題和摘要的文字多少不一,每個 item高度自然就不一樣,我又想用collocationView展示兩列數據,首先想到的就是瀑布流?,F在很多移動端H5就是用瀑布流展示的,比如花瓣、Pinterest(需要梯子)。

1 - 花瓣布局

2 - PinterestAPP布局

本文采用類似花瓣的布局給大家分享,先上一張最終效果圖:
3 - 最終效果圖

因為不管是OC還是Swift中UICollocationView的UICollectionViewFlowLayout默認都是只能設置統一的布局的,要做到如效果圖一樣的效果就需要重寫UICollectionViewFlowLayout。

1 - 分析原理

首先我們迫切需要知道的是每個item高度是多少如何布局。上篇文章我們已經通過HTML解析到的數據計算出了高度,接下來就要通過制定一個規則,讓每個item加在特定一列上。假如我們設定有兩列數據,很顯然我們不能通過列表數組的index的奇偶來確定它們的位置。因為假設數組arr的偶數下標值arr[i]的高度值都很大,奇數下標值arr[i]的高度值都很小,所以通過奇偶布局會出現第一列會比第2列高出很多,第二列下面會留很大的空白,這樣肯定是不行。


4 - 按數組下標從左至右從上到下可能出現的情況

現在給出一種思路:數據數組findList傳入自定義的Layout類,定義一個列高數組columnHeight記錄每一列的總高度,然后定義一個記錄每一列的總item個數的數組columnItemCount,最后定義一個元素類型為UICollectionViewLayoutAttributes的數組attributesArray此數組就是最終item布局的數組。遍歷數據數組findList,拿到當前位置的IndexPath初始化一個UICollectionViewLayoutAttributes對象attributes,找到最短列,把數據追加在最短列,累加高度在columnHeight中的最短列,columnItemCount中最短列的個數+1,拿到前面的數據可以計算每個item的frame,最后把整個對象追加在布局數組attributesArray中。循環結束后,拿到最高列的高度減去最高列每個item間的間隙除以最高列的個數可以計算出平均值,這個值用來設置itemSize的高。最后把attributesArray賦值給self.layoutAttributesArray就可以了。如果要加頭部和尾部就把頭部尾部的布局數據插入頭尾即可。大家可能看的很懵,直接上代碼,注釋很詳細了 :

 //
//  FindFlowLayout.swift
//  SwiftApp
//
//  Created by leeson on 2018/7/4.
//  Copyright ? 2018年 李斯芃 ---> 512523045@qq.com. All rights reserved.
//

import UIKit

class HomeFlowLayout: UICollectionViewFlowLayout {
    // 總列數
    var columnCount:Int = 0
    // 數據數組
    var findList = [JianshuModel]()
    // 整個webview的高度
    private var maxH:Int?
    // 頭部高度
    var headerH:CGFloat = 100
    //所有item的屬性
    fileprivate var layoutAttributesArray = [UICollectionViewLayoutAttributes]()
    
    override func prepare() {
        let contentWidth:CGFloat = (self.collectionView?.bounds.size.width)! - self.sectionInset.left - self.sectionInset.right
        let marginX = self.minimumInteritemSpacing
        let itemWidth = (contentWidth - marginX * CGFloat(self.columnCount - 1)) / CGFloat.init(self.columnCount)
        self.computeAttributesWithItemWidth(CGFloat(itemWidth))
    }
    
    ///根據itemWidth計算布局屬性
    func computeAttributesWithItemWidth(_ itemWidth:CGFloat){
        
        // 定義一個列高數組 記錄每一列的總高度
        var columnHeight = [Int](repeating: Int(self.sectionInset.top + self.headerH), count: self.columnCount)
        // 定義一個記錄每一列的總item個數的數組
        var columnItemCount = [Int](repeating: 0, count: self.columnCount)
        var attributesArray = [UICollectionViewLayoutAttributes]()
        
        // 添加頭部屬性
        let headerAttr:UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes.init(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: IndexPath.init(item: 0, section: 0))
        headerAttr.frame = CGRect(x: 0, y: CGFloat(0), width: self.collectionView!.bounds.size.width, height: self.headerH)
        attributesArray.append(headerAttr)
        // 給屬性數組設置數值
        //self.layoutAttributesArray = attributesArray
        
        // 遍歷數據計算每個item的屬性并布局
        var index = 0
        for data in self.findList {
            
            let indexPath = IndexPath.init(item: index, section: 0)
            let attributes = UICollectionViewLayoutAttributes.init(forCellWith: indexPath)
            // 找出最短列號
            let minHeight:Int = columnHeight.sorted().first!
            let column = columnHeight.index(of: minHeight)
            // 數據追加在最短列
            columnItemCount[column!] += 1
            let itemX = (itemWidth + self.minimumInteritemSpacing) * CGFloat(column!) + self.sectionInset.left
            let itemY = minHeight
            // 等比例縮放 計算item的高度
            let itemH = Int(Double(data.itemHeight!)!)
            // 設置frame
            attributes.frame = CGRect(x: itemX, y: CGFloat(itemY), width: itemWidth, height: CGFloat(itemH))
            
            attributesArray.append(attributes)
            // 累加列高
            columnHeight[column!] += itemH + Int(self.minimumLineSpacing)
            index += 1
        }
        
        // 找出最高列列號
        let maxHeight:Int = columnHeight.sorted().last!
        let column = columnHeight.index(of: maxHeight)
        // 根據最高列設置itemSize 使用總高度的平均值
        let itemH = (maxHeight - Int(self.minimumLineSpacing) * (columnItemCount[column!] + 1)) / columnItemCount[column!]
        self.itemSize = CGSize(width: itemWidth, height: CGFloat(itemH))
        // 添加尾部屬性
        let footerIndexPath:IndexPath = IndexPath.init(item: 0, section: 0)
        let footerAttr:UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes.init(forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, with: footerIndexPath)
        footerAttr.frame = CGRect(x: 0, y: CGFloat(maxHeight), width: self.collectionView!.bounds.size.width, height: 30)
        attributesArray.append(footerAttr)
        // 給屬性數組設置數值
        self.layoutAttributesArray = attributesArray
        self.maxH = maxHeight + 30
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        
        return self.layoutAttributesArray
    }
    
    ///重寫設置contentSize,一定要寫上這個方法,不然可能拉到最底部的時候可能會有很多空白
    override var collectionViewContentSize: CGSize {
        get {
            return CGSize(width: (collectionView?.bounds.width)!, height: CGFloat(self.maxH!))
        }
        set {
            self.collectionViewContentSize = newValue
        }
    }

}

5 - 按如上代碼布局后的位置

2 - 數據展示

知道如何布局了就是常規的collocationView展示了,只是我這里加上了頭部 尾部視圖,還寫了個簡單的上下拉刷新。頭部、尾部、cell我都是用xib寫的,注冊的時候記得用nib注冊就行了。控制器里設置好自定layout的一些屬性:

//MARK: - --- 設置item的布局
    func setHomeFlowLayouts(){
        //通過layout的一些參數設置item的寬度
        let inset = UIEdgeInsetsMake(10, 10, 10, 10)
        let minLine:CGFloat = 10.0
        self.itemWidth = (SCREEN_WIDTH - inset.left - inset.right - minLine * (CGFloat(self.columnCount - 1))) / CGFloat(self.columnCount)
        
        //設置布局屬性
        self.flowLayout.columnCount = self.columnCount
        self.flowLayout.sectionInset = inset
        self.flowLayout.minimumLineSpacing = minLine
    }

還有就是調用網絡請求的方法,這里是沒有用到網絡請求接口,是用的我上篇文章提到的HTML解析得到的數據模型,我在請求模型類JianshuRequestModel.swift里暴露了個方法,待解析完網頁數據存入模型后用閉包(相當于OC的block)返回數組數據:

        //網絡請求回調
        //參數:self.index是頁碼;self.itemWidth是透過計算得到的item的寬度
        JianshuRequestModel.jianshuRequestDataWithPage(self.index, Float(self.itemWidth), { (headInfo) in
              //返回headInfo頭部信息,只有在page等于1的時候返回,因為每一頁的頭部信息都是一樣的。
        }) { (dataArr) in
              //dataArr文章列表數據
        }

至于上面提到的上下拉刷新,也就不贅述了,監聽scrollView.contentOffset.y 和self.collectionView.contentInset設置個臨界點做相應的操作即可,有疑惑的可以參考下文末的Demo。

3 - 切換布局

有運行過demo看過效果的同學可能會看到我在頭部放了一個切換的按鈕。此按鈕的作用是用來切換布局的。默認情況下是兩列,不停點擊會在1列和2列中切換。self.columnCount這個成員變量就是設置列數的,理論上還可以設置3甚至更大的整數,不過這里我不推薦這么做,因為2列以上item的寬度會很窄,數據會擠在一起相當難看。


6 - 切換布局

我在頭部視圖類寫了個按鈕點擊事件的閉包回調,在初始化頭部視圖的地方寫上如下邏輯可切換布局:

            //點擊切換布局
            self.headerView?.switchBack = { (click) in
                print(click)
                //切換列數
                self.columnCount = (click == true) ? 1 : 2
                self.setHomeFlowLayouts()
                //遍歷數組 重新計算高度
                var num = 0
                for model in self.dataArr {
                    //計算標題和摘要的高度
                    model.imgW = Float(self.itemWidth - 16)
                    model.imgH = model.wrap!.count > 0 ? model.imgW! * 120 / 150 : nil
                    model.titleH = GETSTRHEIGHT(fontSize: 20, width: CGFloat(model.imgW!) , words: model.title!) + 1
                    model.abstractH = GETSTRHEIGHT(fontSize: 14, width: CGFloat(model.imgW!) , words: model.abstract!) + 1
                    
                    //item高度
                    var computeH:CGFloat = 8 + 25 + 3 + 10 + 8 + (model.imgH != nil ? CGFloat(model.imgH!) : 0) + 8 + model.titleH! + 8 + model.abstractH! + 8 + 10 + 8
                    //如果沒有圖片減去一個間隙8
                    computeH = computeH - (model.wrap!.count > 0 ? 0 : 8)
                    model.itemHeight = String(format: "%.f", computeH)
                    self.dataArr[num] = model;
                    num += 1
                }
                //重新賦值改變布局
                self.flowLayout.findList = self.dataArr
                //刷新視圖
                self.collectionView?.reloadData()
            }

7 - 切換布局效果圖

以上就是本文的全部內容,不懂的可以下載demo自行運行看下源碼或者可以留言我。
下篇文章我將介紹文章詳情的webview與js的交互,感興趣的可以關注我,有更新會有提醒。
??猛戳右側鏈接下載 本文GitHub源碼
上一篇文章:Swfit爬蟲通過作者ID無接口獲取簡書文章列表,正則匹配HTML標簽存儲模型數據

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容

  • iOS流布局UICollectionView系列一——初識與簡單使用UICollectionView 一、簡介 U...
    我是啊梁閱讀 10,503評論 3 10
  • 運營管理本學期第一次復盤,李萌萌小組強勢來襲,關于海爾集團定制生產的案例分析。
    AliceTrueLife閱讀 131評論 0 1
  • 文/雕琢人生 生活總會變著花樣折騰你,或大或小,或強或弱,磨練心性,或許,這才是真實的生活。 夢里,手癢癢的,居然...
    雕琢人生閱讀 1,540評論 17 14
  • 姓名 :張海滔 公司:揚州市方圓建筑工程有限公司 380期(哈爾濱)《六項精進》“感謝組”成員 【日精進打卡第 3...
    小樓昨夜又西風_a3c5閱讀 105評論 0 0
  • 文/竹之華 時間是一道轉輪,寒來暑往,兜兜轉轉,卻在不經意之間磨平了人的棱角。驀然一驚,已到中年,所有青春的輕狂,...
    大野的竹閱讀 377評論 0 2