iOS帶緩存橫向ScrollView

經常需要用到橫向滑動的控件薄霜,控件里面也有一個個Cell茎芭,就像橫向的UITableView牧牢。發(fā)現網上也有很多橫向UITableView的方案看锉,甚至通過旋轉UITableView來實現橫向滑動。


光醬

  我通常是通過UIScrollView來實現橫向滑動塔鳍,但是一旦加載內容過多就會出現卡頓伯铣。因此我通過添加界面緩存來避免卡頓的問題。還通過模仿UITableView的dataSource來實現面向協(xié)議編程轮纫。

實現步驟

自定義控件HMHorizontalScrollView腔寡。首先通過包含UIScrollView實現橫向滑動。通過添加HMHorizontalScrollCell實現內容填充掌唾。設立一個Cell的緩存數組放前,顯示復用緩存數組中的Cell。

緩存數組的內容需要根據滑動的位置進行位置更新糯彬。緩存的顯示會根據ScrollView的滑動進行改變凭语,保證在滑動到下一個Cell之前,已經有Cell在那個位置等待顯示了撩扒。

最后實現協(xié)議編程似扔,通過配置DataSource來傳遞界面顯示配置,通過協(xié)議的返回值設置顯示內容。通過Delegate首先點擊事件炒辉。

類圖

緩存原理

定義一個數組豪墅,將顯示界面放入數組中。每次滑動的時候重新計算緩存界面的顯示位置黔寇,改變未顯示出來的界面的位置偶器。

假如一個屏幕可以顯示3個Cell的話,可能最多會顯示4個Cell缝裤,這個時候就需要在前后多加一個Cell屏轰,用來預加載界面,這樣在快速滑動的時候就會更順暢倘是。通過不斷更新這些顯示的Cell的位置和信息亭枷,就可以完成緩存機制袭艺。

示意圖

當向左滑時搀崭,當4號Cell滑出屏幕時,就把5號Cell移到第一個的位置猾编。
  當向右滑時瘤睹,當1號Cell滑出屏幕時,就把0號Cell移到最后一個的位置答倡。
  當滑到最左邊或最右邊時轰传,就保持數據原始的順序,不再做改變瘪撇。

extension HMHorizontalScrollView: UIScrollViewDelegate {

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    // 第一次次進來會可能向下滑動64
    scrollView.contentOffset.y = 0
    
    if numberOfCacheView < maxNumberOfCacheView {
        return
    }
    
    if scrollView.contentOffset.x < 0 {
        // 滑到第一個
        resetItemViewOfStart()
        
    } else if scrollView.contentOffset.x + scrollView.w > scrollView.contentSize.width {
        // 滑到最后一個
        resetItemViewOfEnd()
        
    } else if cells[1].frame.origin.x + cells[0].w + separatorWidth < scrollView.contentOffset.x  {
        // 左滑
        resetItemViewOfLeftPan()
        // 更新最后一個顯示
        let lastCellIndex = Int((cells.last?.frame.origin.x ?? 0) / (cellSize.width + separatorWidth))
        if lastCellIndex > 0 && lastCellIndex < numberOfCells {
            _ = dataSource?.horizontalScrollView(in: self, cellAt: lastCellIndex)
        }
        
    } else if cells[numberOfCacheView - 2].frame.origin.x > scrollView.contentOffset.x + scrollView.frame.width {
        // 右滑
        resetItemViewOfRightPan()
        // 更新第一個顯示
        let firstCellIndex = Int((cells.first?.frame.origin.x ?? 0) / (cellSize.width + separatorWidth))
        if firstCellIndex > 0 && firstCellIndex < numberOfCells {
            _ = dataSource?.horizontalScrollView(in: self, cellAt: firstCellIndex)
        }
    }
    
}

// 滑到第一個
func resetItemViewOfStart() {
    
    hm_for(cells) { cell, index in
        
        cell.x = separatorWidth + (cellSize.width + separatorWidth) * CGFloat(index)
        
        if let index = indexOf(x: cell.x) {
            _ = dataSource?.horizontalScrollView(in: self, cellAt: index)
        }
    }
}

// 滑到最后一個
func resetItemViewOfEnd() {
    
    hm_for(cells) { cell, index in
        cell.x = scrollView.contentSize.width - (cellSize.width + separatorWidth) * CGFloat(numberOfCacheView - index)
        
        if let index = indexOf(x: cell.x) {
            _ = dataSource?.horizontalScrollView(in: self, cellAt: index)
        }
    }
}

// 左滑
func resetItemViewOfLeftPan() {
    
    let temp = cells.first!
    temp.x = cells.last!.x + (cellSize.width + separatorWidth)
    
    hm_for(cells) { (cell, index) in
        if index < cells.count - 1 {
            cells[index] = cells[index + 1]
        } else {
            cells[index] = temp
        }
    }
}

// 右滑
func resetItemViewOfRightPan() {
    
    let temp = cells.last!
    temp.x = cells.first!.x - (cellSize.width + separatorWidth)
    
    hm_for(cells) { (cell, index) in
        if index < cells.count - 1 {
            cells[cells.count - index - 1] = cells[cells.count - index - 2]
        } else {
            cells[cells.count - index - 1] = temp
        }
    }
}

func indexOf(x: CGFloat) -> Index? {
    
    if cellSize.width + separatorWidth != 0 {
        return Int(x / (cellSize.width + separatorWidth))
    }
    
    return nil
}
}

面向協(xié)議編程

定義一個HMHorizontalScrollViewDataSource協(xié)議获茬,這個協(xié)議主要是為了獲取數據源信息。設置一個dataSource的委托倔既,通過委托在實現界面配置數據恕曲。當需要某些數據的時候,dataSource實現協(xié)議獲取返回值渤涌,返回值就是需要的數據佩谣。

  • 協(xié)議

    protocol HMHorizontalScrollViewDataSource:     NSObjectProtocol {
    // cell數量
    func numberOfCells(in horizontalScrollView: HMHorizontalScrollView) -> Int
    // 自身的寬度
    func viewWidth(in horizontalScrollView: HMHorizontalScrollView) -> CGFloat
    // cell的寬度
    func horizontalScrollView(_ horizontalScrollView: HMHorizontalScrollView, cellSizeAt index: Index) -> CGSize
    // 返回cell
    func horizontalScrollView(in horizontalScrollView: HMHorizontalScrollView, cellAt index: Index) -> HMHorizontalScrollCell
    }
    
  • 協(xié)議使用

    // cell的數量
    fileprivate var numberOfCells: Int {
      return dataSource?.numberOfCells(in: self) ?? 0
    }
    // cell尺寸
    fileprivate var cellSize: CGSize {
      return dataSource?.horizontalScrollView(self, cellSizeAt: 0) ?? CGSize()
    }
    // 界面寬度
    fileprivate var viewWidth: CGFloat {
      return dataSource?.viewWidth(in: self) ?? 0
    }
    

當顯示界面的Cell很少時,就不需要緩存了实蓬,需要特殊處理茸俭。

當ScrollView滑動到邊緣時,也需要重新計算Cell排布安皱,不然隱藏的緩存Cell就會顯示出來调鬓。

需要考慮邊緣條件。

通過dataSource配置HMHorizontialView的數據源酌伊,HMHorizontialView內部通過復用顯示Cell袖迎,可以在一次加載多個Cell而不卡頓。
  UIScollView可以實現橫向滑動的分頁顯示,但是一次加載過多的內容會出現卡頓燕锥,通過復用顯示的界面來避免卡頓辜贵。并通過仿照UITableView的
  dataSource實現面向協(xié)議的編程。

最后有木有人推薦個工作機會归形!

Demo地址:
https://github.com/hm306599934/HMHorizontalScrollView/tree/master

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末托慨,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子暇榴,更是在濱河造成了極大的恐慌厚棵,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔼紧,死亡現場離奇詭異婆硬,居然都是意外死亡,警方通過查閱死者的電腦和手機奸例,發(fā)現死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門彬犯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人查吊,你說我怎么就攤上這事谐区。” “怎么了逻卖?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵宋列,是天一觀的道長。 經常有香客問我评也,道長炼杖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任盗迟,我火速辦了婚禮坤邪,結果婚禮上,老公的妹妹穿的比我還像新娘诈乒。我一直安慰自己罩扇,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布怕磨。 她就那樣靜靜地躺著喂饥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肠鲫。 梳的紋絲不亂的頭發(fā)上员帮,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音导饲,去河邊找鬼捞高。 笑死氯材,一個胖子當著我的面吹牛,可吹牛的內容都是我干的硝岗。 我是一名探鬼主播氢哮,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼型檀!你這毒婦竟也來了冗尤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤胀溺,失蹤者是張志新(化名)和其女友劉穎裂七,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體仓坞,經...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡背零,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了无埃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徙瓶。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖录语,靈堂內的尸體忽然破棺而出倍啥,到底是詐尸還是另有隱情禾乘,我是刑警寧澤澎埠,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站始藕,受9級特大地震影響蒲稳,放射性物質發(fā)生泄漏。R本人自食惡果不足惜伍派,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一江耀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诉植,春花似錦祥国、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至灼擂,卻和暖如春壁查,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剔应。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工睡腿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留语御,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓席怪,卻偏偏與公主長得像应闯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挂捻,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內容

  • ## iOS常用問題總結#### iOS基礎知識回顧##### 1孽锥、為什么說Objective-C是一門動態(tài)的語言...
    蟬始鳴閱讀 464評論 0 3
  • 作者si1ence2016.05.20 10:24* http://www.reibang.com/p/bc3f8...
    Kiddz閱讀 1,193評論 0 12
  • 為了寫這篇文章惜辑,早上九點半剛過,我就在金科世界走廊拍下了這張照片疫赎。在橫店電影城門口貼著一張海報盛撑,宣傳今天播放的電影...
    若雨菲竹閱讀 100評論 0 0
  • 第一步:認知(工具:生命數字。)3號下午捧搞。 課程形式: 以學員中一人生命數字的示范抵卫,給到大家生命數字計算的方法,能...
    谷應閱讀 171評論 0 0
  • 白云山能仁寺如峰師父胎撇,說了一年叫我送張畫給他介粘,今天終于上山完成了我的心愿,謝謝如峰師父的欣賞和喜歡晚树!感恩姻采!br>
    李伍香閱讀 206評論 0 4