詳細(xì)寫(xiě)一個(gè)功能完善的PhotoBrowser(二)加載多張圖片以及加載網(wǎng)絡(luò)圖片

寫(xiě)一個(gè)功能完善的圖片瀏覽器

最終效果 源碼

網(wǎng)絡(luò).gif

本地圖片.gif

本篇文章后完成的效果

本篇效果.gif

前言: 在之前我們已經(jīng)實(shí)現(xiàn)了對(duì)單張圖片的手勢(shì)處理縮放,如果順利的話 , 我們的photoView目前對(duì)于單張圖片的功能支持已經(jīng)很好了, 接下來(lái)我們來(lái)實(shí)現(xiàn)對(duì)多張圖片的瀏覽, 實(shí)現(xiàn)真正意義上的圖片瀏覽器, 同時(shí)會(huì)用到kingfisher(實(shí)現(xiàn)了和SDWebImage相似的功能的一個(gè)加載圖片的swift框架 -- 王巍寫(xiě))來(lái)加載網(wǎng)絡(luò)圖片

分析

1.實(shí)現(xiàn)多張圖片的滾動(dòng)瀏覽, 相信大家都會(huì)有思路怎么實(shí)現(xiàn)的, 因?yàn)榫褪窍喈?dāng)于一個(gè)圖片輪播器 , 而且還不需要自動(dòng)滾動(dòng), 甚至不需要實(shí)現(xiàn)循環(huán)滾動(dòng), 那么應(yīng)該是很簡(jiǎn)單就能實(shí)現(xiàn)的

  • 使用UIScrollView來(lái)實(shí)現(xiàn)圖片的滾動(dòng), 那么在這個(gè)過(guò)程中就需要注意到循環(huán)利用ImageView的處理,否則會(huì)浪費(fèi)很多的內(nèi)存容易造成內(nèi)存爆滿, 你可以使用兩個(gè)或者三個(gè)ImageView來(lái)實(shí)現(xiàn), 具體的思路分析和實(shí)現(xiàn)可以參考這里, 或者參考MJPhotoBroswer自己來(lái)管理一個(gè)ImageView的重用機(jī)制

  • 從上面使用UIScrollView的分析中感覺(jué)到,要手動(dòng)來(lái)實(shí)現(xiàn)重用還是需要做不少的工作, 這里筆者希望比較簡(jiǎn)單高效的實(shí)現(xiàn)PhotoBroswer, 所以選擇了使用UICollectionView來(lái)實(shí)現(xiàn), 因?yàn)樗詭в兄赜脵C(jī)制, 我們可以直接拿來(lái)使用, 如果不是很熟悉collectionView的使用,也不用太擔(dān)心, 本次不會(huì)用到它的很多高級(jí)的功能, 不過(guò)后面會(huì)提到一點(diǎn)collectionView的分頁(yè)使用技巧

2 . 實(shí)現(xiàn)網(wǎng)絡(luò)圖片的加載

  • 其實(shí)要很簡(jiǎn)單加載服務(wù)器的圖片, 使用apple提供給我們的一些API就可以很簡(jiǎn)單把圖片"加載"處理, 不過(guò)需要注意的是我們提到的只是能夠"加載"出來(lái), 但是其中還有很多的細(xì)節(jié)需要處理, 比如,

a.你應(yīng)該考慮異步加載圖片不要阻塞主線程, 那么當(dāng)有多張圖片的時(shí)候,你需要處理多個(gè)線程的開(kāi)銷(xiāo)和效率.
b. 對(duì)于加載完成的圖片你應(yīng)該考慮緩存, 以便于之后能很快加載, 那么緩存你需要處理"內(nèi)存緩存"(臨時(shí)緩存到內(nèi)存,加載速度很快, 但是緩存多張圖片到內(nèi)存的時(shí)候會(huì)消耗大量的內(nèi)存, 所以需要管理緩存到內(nèi)存的文件大小, 及時(shí)清空緩存)和"磁盤(pán)緩存"(持久化保存在沙盒)
相信僅僅是上面提到兩點(diǎn), 一定會(huì)讓大多數(shù)的讀者感覺(jué)到很難實(shí)現(xiàn)(是的, 鑒于筆者也感覺(jué)到自己實(shí)現(xiàn)這個(gè)網(wǎng)絡(luò)圖片的加載的困難和自己的能力有限),所以我們應(yīng)該考慮其他的方便的方法來(lái)實(shí)現(xiàn), 當(dāng)然上面給出了自己實(shí)現(xiàn)的思路, 有能力的朋友不妨自己去實(shí)現(xiàn)一下

  • 既然自己實(shí)現(xiàn)很麻煩, 就找第三方來(lái)幫忙了, 這里使用到了"王巍"寫(xiě)的"kingfisher", 這個(gè)純swift的圖片加載庫(kù)提供了和SDWebImage相類(lèi)似的接口使用很是方便, 同時(shí)很驚訝的是這個(gè)框架較好的實(shí)現(xiàn)了GIF圖片的加載(關(guān)于GIF圖片的加載后面可能會(huì)提到怎么去實(shí)現(xiàn))

注意, 在使用kingfisher加載多張網(wǎng)絡(luò)圖片的時(shí)候, 你可能會(huì)注意到, xcode上面顯示的內(nèi)存消耗是很大的, 在實(shí)現(xiàn)PhotoBrowser的時(shí)候, 我使用SDWebImage和Kingfisher加載了相同的圖片, 發(fā)現(xiàn)在xcode上面顯示的內(nèi)存消耗兩者確實(shí)是相差很大的, 你會(huì)明顯的發(fā)現(xiàn)kingfisher消耗了比SDWebImage多很多的內(nèi)存, 所以筆者當(dāng)時(shí)去打擾了一下王巍, 他說(shuō)到這兩個(gè)框架的實(shí)現(xiàn)思路是相似的, 內(nèi)存消耗上不應(yīng)該有很大的區(qū)別, 可能是xcode自身顯示的bug, 后來(lái)我用真機(jī)測(cè)試多張圖片確實(shí)是沒(méi)有收到內(nèi)存警告, 所以大家可以放心的使用

思路寫(xiě)的比較啰嗦, 下面進(jìn)入實(shí)現(xiàn)部分

1). 自定義UICollectionViewCell用于展示每一張圖片

  • 新建文件PhotoViewCell, 這里直接把之前的PhotoView的代碼拿過(guò)來(lái)稍作改變就可以利用之前詳細(xì)寫(xiě)一個(gè)功能完善的PhotoBrowser同時(shí)支持GIF(一)里寫(xiě)的對(duì)單張圖片的處理, 因?yàn)橹皇菗Q了一個(gè)容器而已
  • 新建文件PhotoModel來(lái)作為圖片模型, 因?yàn)槊恳粋€(gè)cell顯示一張圖片, 所以它擁有這張圖片的photoModel


    photoModel.png
這里在設(shè)置了photoModel的時(shí)候, 我么利用屬性觀察器來(lái)設(shè)置image

    private func setupImage() {
        // 首先判斷是否正確設(shè)置了photoModel
        guard let photo = photoModel else {
            assert(false, "設(shè)置的圖片模型不正確")
            return
        }
        
        // 如果是加載本地的圖片, 直接設(shè)置圖片即可, 注意這里是photoBrowser需要提升的地方
        // 因?yàn)閷?duì)于本地圖片的加載沒(méi)有做處理, 所以當(dāng)直接使用 UIImage(named"")的形式加載圖片的時(shí)候, 會(huì)消耗大量的內(nèi)存
        // 不過(guò)鑒于參考了其他的圖片瀏覽器框架, 大家對(duì)本地圖片都沒(méi)有處理, 因?yàn)檫@個(gè)確實(shí)用的很少, 畢竟都是用來(lái)加載網(wǎng)絡(luò)圖片的情況比較多
        // 如果發(fā)現(xiàn)確實(shí)需要處理后面會(huì)努力處理這個(gè)問(wèn)題
        if photo.localImage != nil {
            // 注意這個(gè)image的屬性觀察器中, 我處理了imageView的frame
            image = photo.localImage
            // 加載完成后直接返回
            return
        }
        
        // 加載網(wǎng)路圖片, 首先判斷url是否合法
        guard let urlString = photo.imageUrlString, let url = NSURL(string: urlString) else {
            assert(false, "設(shè)置的url不合法")
            return
        }
        // 設(shè)置默認(rèn)圖片
        if let sourceImageView = photo.sourceImageView {
            image = sourceImageView.image
        }
        // 如果沒(méi)有提供默認(rèn)的圖片, 就設(shè)置一張默認(rèn)的圖片
        image =  image ?? UIImage(named: "2")

        // 這里使用kingfisher來(lái)加載網(wǎng)絡(luò)圖片 很簡(jiǎn)單的調(diào)用
        imageView.kf_setImageWithURL(url, placeholderImage: image, optionsInfo: nil, progressBlock: {[weak self] (receivedSize, totalSize) in
            let progress = Double(receivedSize) / Double(totalSize)
            //            print(progress)
            // 這里面能夠獲取到加載進(jìn)度, 便于提供進(jìn)度條顯示
            
        }) {[weak self] (image, error, cacheType, imageURL) in
            // 加載完成
            // 注意: 因?yàn)檫@個(gè)閉包是多線程調(diào)用的 所以可能存在 沒(méi)有顯示完圖片,就點(diǎn)擊了返回
            // 這個(gè)時(shí)候self已經(jīng)被銷(xiāo)毀了 所以使用[unonwed self] 將會(huì)導(dǎo)致"野指針"的問(wèn)題
            // 使用 [weak self] 保證安全訪問(wèn)self
            // 但是這也不是絕對(duì)安全的, 比如在 self 銷(xiāo)毀之前, 進(jìn)入了這個(gè)閉包 那么strongSelf 有值 進(jìn)入
            // 如果在這時(shí)恰好 self 銷(xiāo)毀了,那么之后調(diào)用strongSelf 都將會(huì)出錯(cuò)crash
            // 可以考慮使用withExtendedLifetime
            //            withExtendedLifetime(self, { () -> self in
            //
            //            })
            if let strongSelf = self  {
                // 加載完成, 設(shè)置圖片, 觸發(fā)里面的屬性觀察器設(shè)置imageView
                strongSelf.image = image
                if let _ = image { return }
                // 提示加載錯(cuò)誤
            }
        }
    }
  1. 新建文件PhotoBroswer來(lái)處理多張圖片的顯示
  • 設(shè)置collection view
    private lazy var collectionView: UICollectionView = {[unowned self] in
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.scrollDirection = .Horizontal
        // 每個(gè)cell的尺寸  -- 寬度設(shè)置為UICollectionView.bounds.size.width ---> 滾一頁(yè)就是一個(gè)完整的cell
        flowLayout.itemSize = CGSize(width: self.zj_width + PhotoBrowser.contentMargin, height: self.zj_height)
        flowLayout.minimumLineSpacing = 0.0
        flowLayout.minimumInteritemSpacing = 0.0
        flowLayout.sectionInset = UIEdgeInsetsZero
        
        // 分頁(yè)每次滾動(dòng) UICollectionView.bounds.size.width
        let collectionView = UICollectionView(frame: CGRect(x: 0.0, y: 0.0, width: self.zj_width + PhotoBrowser.contentMargin, height: self.zj_height), collectionViewLayout: flowLayout)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.pagingEnabled = true
        collectionView.registerClass(PhotoViewCell.self, forCellWithReuseIdentifier: PhotoBrowser.cellID)
        self.insertSubview(collectionView, atIndex: 0)
        
        return collectionView
    }()
  • 處理collection view的代理和datasource方法, 這里面比較容易理解
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return photoModels.count
    }
    
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoBrowser.cellID, forIndexPath: indexPath) as! PhotoViewCell
        // 避免出現(xiàn)重用出錯(cuò)的問(wèn)題, 大家可以試下注釋這行會(huì)帶來(lái)什么不想要的效果, 然后應(yīng)該就理解了這個(gè)方法為何存在
        cell.resetUI()
        let currentModel = photoModels[indexPath.row]
        
        // 注意之前直接傳了self的一個(gè)函數(shù)給singleTapAction 造成了循環(huán)引用
        cell.singleTapAction = {[unowned self](ges: UITapGestureRecognizer) in
            self.dismiss()
        }
        
        return cell
    }
    
    // 這里監(jiān)控collectionView的滾動(dòng), 是希望在滾動(dòng)超過(guò)一半的時(shí)候改更改圖片的索引, 這個(gè)會(huì)在之后的toolBar上使用到, 來(lái)顯示索引
    func scrollViewDidScroll(scrollView: UIScrollView) {
        // 向下取整
        currentIndex = Int(scrollView.contentOffset.x / scrollView.zj_width + 0.5)

    }
  1. 前面提到的處理collection view的分頁(yè)的一點(diǎn)小技巧
  • 如果你只是簡(jiǎn)單設(shè)置了collectionView.pagingEnabled = true,設(shè)置 flowLayout.itemSize = CGSize(width: self.zj_width , height: self.zj_height), 并且設(shè)置cell里面的scrollView和contentView的尺寸相同, 那么滾動(dòng)的效果是這樣的
沒(méi)有間隙.png

我們希望兩張圖片之間有一定的間隙, 那么很直接, 直接將cell里的scrollView的寬度減少一點(diǎn)應(yīng)該就可以了

    /// 懶加載
    lazy var scrollView: UIScrollView = {
        let scrollView = UIScrollView(frame: CGRect(x: 0.0, y: 0.0, width: self.contentView.zj_width - PhotoBrowser.contentMargin, height: self.contentView.zj_height))
間隙.gif

這樣圖片之間的間隙自然是出來(lái)的, 但是發(fā)現(xiàn)滾動(dòng)完成后后面的圖片顯示不正常. 因?yàn)閏ollectionView每次滾動(dòng)一頁(yè)的寬度是UICollectionView.bounds.size.width, 所以和cell的尺寸沒(méi)有關(guān)系, 那么我們?cè)偬幚硪幌?/p>

分頁(yè)分析.png
        // 每個(gè)cell的尺寸  -- 寬度設(shè)置為UICollectionView.bounds.size.width ---> 滾一頁(yè)就是一個(gè)完整的cell
        flowLayout.itemSize = CGSize(width: self.zj_width + PhotoBrowser.contentMargin, height: self.zj_height)

    /// cell中scrollView的尺寸
        let scrollView = UIScrollView(frame: CGRect(x: 0.0, y: 0.0, width: self.contentView.zj_width - PhotoBrowser.contentMargin, height: self.contentView.zj_height))

        // 分頁(yè)每次滾動(dòng) UICollectionView.bounds.size.width
        let collectionView = UICollectionView(frame: CGRect(x: 0.0, y: 0.0, width: self.zj_width + PhotoBrowser.contentMargin, height: self.zj_height), collectionViewLayout: flowLayout)

到目前為止, 多張圖片的顯示以及網(wǎng)絡(luò)圖片的加載處理基本就完整了,一個(gè)比較成型的PhotoBrowser就完成了, 至于里面的toolBar和提示框, 還有過(guò)渡動(dòng)畫(huà)可能會(huì)在以后寫(xiě)寫(xiě),歡迎關(guān)注


詳細(xì)請(qǐng)移步源碼,里面都有詳細(xì)的Demo使用示例 如果您覺(jué)得有幫助,不妨給個(gè)star鼓勵(lì)一下, 歡迎關(guān)注


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末珊膜,一起剝皮案震驚了整個(gè)濱河市窗骑,隨后出現(xiàn)的幾起案子北启,更是在濱河造成了極大的恐慌膘盖,老刑警劉巖感憾,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件山析,死亡現(xiàn)場(chǎng)離奇詭異订框,居然都是意外死亡链嘀,警方通過(guò)查閱死者的電腦和手機(jī)懂版,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)鹃栽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人躯畴,你說(shuō)我怎么就攤上這事民鼓。” “怎么了私股?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵摹察,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我倡鲸,道長(zhǎng)供嚎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任峭状,我火速辦了婚禮克滴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘优床。我一直安慰自己劝赔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布胆敞。 她就那樣靜靜地躺著着帽,像睡著了一般杂伟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仍翰,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天赫粥,我揣著相機(jī)與錄音,去河邊找鬼予借。 笑死越平,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灵迫。 我是一名探鬼主播秦叛,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瀑粥!你這毒婦竟也來(lái)了挣跋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤利凑,失蹤者是張志新(化名)和其女友劉穎浆劲,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體哀澈,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年度气,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了割按。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡磷籍,死狀恐怖适荣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情院领,我是刑警寧澤弛矛,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站比然,受9級(jí)特大地震影響丈氓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜强法,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一万俗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧饮怯,春花似錦闰歪、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)临扮。三九已至,卻和暖如春教翩,著一層夾襖步出監(jiān)牢的瞬間公条,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工迂曲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留靶橱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓路捧,卻偏偏與公主長(zhǎng)得像关霸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杰扫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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

  • 因?yàn)橐Y(jié)局swift3.0中引用snapKit的問(wèn)題,看到一篇介紹Xcode8,swift3變化的文章,覺(jué)得很詳細(xì)...
    uniapp閱讀 4,426評(píng)論 0 12
  • 前言 iOS里的UI控件其實(shí)沒(méi)有幾個(gè)队寇,界面基本就是圍繞那么幾個(gè)控件靈活展開(kāi),最難的應(yīng)屬UICollectionVi...
    alenpaulkevin閱讀 31,797評(píng)論 9 175
  • 昨天晚上突如其來(lái)的生病章姓,突然感到自己好脆弱佳遣,在那種特別疼痛的狀態(tài)下,心里打翻了五味瓶凡伊,不是滋味零渐,去急診的路上 心里...
    昊子_011c閱讀 197評(píng)論 0 0
  • 本人黨輝,生于1974年系忙,父親是一名軍轉(zhuǎn)干部從小就繼承了不屈不撓不服輸?shù)能娙司袼信巍T诓粩啻蚱吹牡缆飞弦恢奔?lì)著我。...
    黨義強(qiáng)閱讀 402評(píng)論 0 1
  • 這也是多年前的事情了银还,我每次想起來(lái)都莫名的傷感风宁。 當(dāng)時(shí)工作在上海,壓力不大蛹疯,薪水不高戒财,工作輕松。上海當(dāng)時(shí)ADSL剛...
    花開(kāi)遍野閱讀 291評(píng)論 0 2