collectionView實(shí)現(xiàn)無限循環(huán)滾動(dòng)卡片

github源碼地址

效果展示
show.gif

前言

去年因?yàn)轫?xiàng)目中有個(gè)切換學(xué)校的功能塑娇,要求以卡片浮動(dòng)效果展示,并且能夠無限循環(huán)滾動(dòng)。

之前找了個(gè)demo它是通過自定義view動(dòng)畫實(shí)現(xiàn)的淋样,卡片數(shù)量多的時(shí)候會(huì)比較卡頓作岖,所以研究了一下唆垃,自己造了一個(gè),現(xiàn)在把實(shí)現(xiàn)的思路分享一下~

好痘儡,開始正題~~~
技術(shù)調(diào)研

在實(shí)現(xiàn)之前辕万,首先我想到的是如何實(shí)現(xiàn)無限循環(huán)的問題,以及多數(shù)量時(shí)如何復(fù)用的問題沉删。關(guān)于view的無限循環(huán)滾動(dòng)和復(fù)用問題渐尿,最經(jīng)典的就是輪播圖了,現(xiàn)在比較常用的兩種方法就是UIScrollView或者UICollectionView實(shí)現(xiàn)的矾瑰。

然后我決定選用UICollectionView涡戳,因?yàn)槲覀冎恍枰o它提供數(shù)據(jù)源驅(qū)動(dòng),它自己就可以復(fù)用脯倚,實(shí)現(xiàn)應(yīng)該比較簡(jiǎn)單渔彰。并且UICollectionViewlayout的靈活性非常強(qiáng),完全可以自定義出很多酷炫的效果推正。

橫向滾動(dòng)

設(shè)置滾動(dòng)方向?yàn)樗交型浚梅猪搶傩?默認(rèn)就是false),這樣就可以橫向流暢滑動(dòng)了

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
collectionView.isPagingEnabled = false // default NO

無限循環(huán)

定義數(shù)組imageArr來存儲(chǔ)圖片作為數(shù)據(jù)源植榕,在每次滾動(dòng)結(jié)束后再沧,都重新定位到第一張。

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
   collectionView.scrollToItem(at: IndexPath(item: 0, section: 0), at:.centeredHorizontally, animated: false)
}

但是每次重定位的時(shí)候都會(huì)出現(xiàn)明顯的突兀效果尊残,所以我們可以把這個(gè)數(shù)組中的圖片復(fù)制100份炒瘸、200份、1000份寝衫。顷扩。。這樣就有足夠多的數(shù)據(jù)來供用戶滑動(dòng)期間來展示了慰毅。

但是直接存儲(chǔ)圖片的話隘截,這個(gè)數(shù)組肯定會(huì)占用很大的內(nèi)存,所以我們開辟一個(gè)新的數(shù)組indexArr,來存儲(chǔ)imageArr中圖片的下標(biāo)婶芭,因?yàn)橹皇谴鎯?chǔ)的是int东臀,所以占用的內(nèi)存是非常小的。

// 初始化數(shù)據(jù)源
imageArr = ["num_1", "num_2", "num_3", "num_4", "num_5"]
for _ in 0 ..< 100 {
    for j in 0 ..< imageArr.count {
        indexArr.append(j)
    }
}

比如imageArr中是["pic1", "pic2", "pic3"]犀农,那么indexArr中就是[0,1,2,0,1,2,0,1,2...]惰赋,這樣就可以把數(shù)據(jù)源設(shè)置為indexArr。

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return indexArr.count
}

代碼初始時(shí)我們把collectionView定位到中間那組(第50組呵哨,假設(shè)共設(shè)置了100組)赁濒,每次滾動(dòng)結(jié)束后也再次定位到中間組的第N張(N:上次滑動(dòng)結(jié)束時(shí)的那張)。

// 定位到 第50組(中間那組)
override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.scrollToItem(at: IndexPath(item: 50 * imageArr.count, section: 0) , at: UICollectionViewScrollPosition.centeredHorizontally, animated: false)
}

每次動(dòng)畫停止時(shí)仇穗,也重新定位到 第50組(中間那組) 模型

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    collectionView.scrollToItem(at: IndexPath(item: 50 * imageArr.count, section: 0) , at: UICollectionViewScrollPosition.centeredHorizontally, animated: false)
}

每張卡片(cell)的數(shù)據(jù)填充

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(CyclicCardCell.self), for: indexPath) as! CyclicCardCell
    let index = indexArr[indexPath.row]
    cell.index = index
    cell.cardImgView.image = UIImage(named: imageArr[index])
    cell.cardNameLabel.text = "奔跑吧流部,小蝸牛~"
    return cell
}

滾動(dòng)動(dòng)畫的平滑過渡

在每次重定位的時(shí)候,雖然設(shè)置的是回到中間組的對(duì)應(yīng)下標(biāo)的那個(gè)cell纹坐,并且animated也是設(shè)置的false枝冀,但是依然可以看出動(dòng)畫有些不連貫、突兀耘子,顯得不流暢自然果漾。

這就需要我們自定義UICollectionViewFlowLayout,來重寫targetContentOffset方法谷誓,通過遍歷目標(biāo)區(qū)域中的cell绒障,來計(jì)算出距離中心點(diǎn)最近的cell,把它調(diào)整到中間捍歪,實(shí)現(xiàn)平緩流暢的滑動(dòng)結(jié)束的效果户辱。

override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        
        let targetRect = CGRect(x: proposedContentOffset.x, y: 0.0, width:  self.collectionView!.bounds.size.width, height: self.collectionView!.bounds.size.height)
        // 目標(biāo)區(qū)域中包含的cell
        let attriArray = super.layoutAttributesForElements(in: targetRect)! as [UICollectionViewLayoutAttributes]
        // collectionView落在屏幕中點(diǎn)的x坐標(biāo)
        let horizontalCenterX = proposedContentOffset.x + (self.collectionView!.bounds.width / 2.0)
        var offsetAdjustment = CGFloat(MAXFLOAT)
        for layoutAttributes in attriArray {
            let itemHorizontalCenterX = layoutAttributes.center.x
            // 找出離中心點(diǎn)最近的
            if(abs(itemHorizontalCenterX-horizontalCenterX) < abs(offsetAdjustment)){
                offsetAdjustment = itemHorizontalCenterX-horizontalCenterX
            }
        }
        
        //返回collectionView最終停留的位置
        return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
    }

卡片的縮放動(dòng)畫

在自定義的UICollectionViewFlowLayout中重寫layoutAttributesForElements方法,通過該方法糙臼,可以獲取到可視范圍內(nèi)的cell庐镐。

然后在cell的滑動(dòng)過程中,通過cell偏移的距離來進(jìn)行尺寸的縮放系數(shù)的設(shè)置变逃,處于最中心的系數(shù)為1必逆,則為原本的大小,隨著中心距離的偏移揽乱,系數(shù)會(huì)逐漸變小為0.98名眉,0.95,0.8...

因此卡片也會(huì)隨之變小凰棉,從而達(dá)到在滾動(dòng)過程中损拢,滾動(dòng)至中間的卡片最大,旁邊的變小的效果渊啰。

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        
        let array = super.layoutAttributesForElements(in: rect)
        var visibleRect = CGRect()
        visibleRect.origin = self.collectionView!.contentOffset
        visibleRect.size = self.collectionView!.bounds.size
        
        for attributes in array!{
            let distance = visibleRect.midX - attributes.center.x
            let normalizedDistance = abs(distance/ActiveDistance)
            let zoom = 1 - ScaleFactor * normalizedDistance
            attributes.transform3D = CATransform3DMakeScale(1.0, zoom, 1.0)
            attributes.zIndex = 1
//            let alpha = 1 - normalizedDistance
//            attributes.alpha = alpha
        }
        return array
    }

OK探橱,至此主要的核心實(shí)現(xiàn)就完成了申屹,可能有些地方思路表述的可能不是太清楚绘证,大家可以去github上下載源碼看下隧膏,有不對(duì)的地方,歡迎指正~

9月28號(hào)更新:

  • 因?yàn)檫@個(gè)文章是第一次寫的嚷那,所以之前表述的不大好胞枕,今天重新修改了一下,希望能對(duì)大家有所幫助??

  • 剛寫了一個(gè)類似的重疊卡片滾動(dòng)的動(dòng)畫魏宽,有興趣的可以看下:

重疊卡片滾動(dòng)動(dòng)畫
ScrollCard.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腐泻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子队询,更是在濱河造成了極大的恐慌派桩,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚌斩,死亡現(xiàn)場(chǎng)離奇詭異铆惑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)送膳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門员魏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叠聋,你說我怎么就攤上這事撕阎。” “怎么了碌补?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵虏束,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我厦章,道長(zhǎng)镇匀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任闷袒,我火速辦了婚禮坑律,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘囊骤。我一直安慰自己晃择,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布也物。 她就那樣靜靜地躺著宫屠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滑蚯。 梳的紋絲不亂的頭發(fā)上浪蹂,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天抵栈,我揣著相機(jī)與錄音,去河邊找鬼坤次。 笑死古劲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缰猴。 我是一名探鬼主播产艾,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼滑绒!你這毒婦竟也來了闷堡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤疑故,失蹤者是張志新(化名)和其女友劉穎杠览,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纵势,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踱阿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吨悍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扫茅。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖育瓜,靈堂內(nèi)的尸體忽然破棺而出葫隙,到底是詐尸還是另有隱情,我是刑警寧澤躏仇,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布恋脚,位于F島的核電站,受9級(jí)特大地震影響焰手,放射性物質(zhì)發(fā)生泄漏糟描。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一书妻、第九天 我趴在偏房一處隱蔽的房頂上張望船响。 院中可真熱鬧,春花似錦躲履、人聲如沸见间。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽米诉。三九已至,卻和暖如春篷帅,著一層夾襖步出監(jiān)牢的瞬間史侣,已是汗流浹背拴泌。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惊橱,地道東北人蚪腐。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像李皇,于是被迫代替她去往敵國(guó)和親削茁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宙枷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫掉房、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,119評(píng)論 4 61
  • 雨,滴嗒滴嗒地拍打著玻璃诅病,像是在彈奏一首悅耳動(dòng)聽的小曲哪亿,撥動(dòng)著我的心弦。抿了一口手上溫?zé)岬目Х认桶省蝇棉?吹綁Ρ谏系奈遥?..
    今在否閱讀 207評(píng)論 0 0
  • 又好多天沒有更新每日一畫芥永,不是我沒有堅(jiān)持每日一畫篡殷,而是忙得沒有時(shí)間每日一寫。 文字這個(gè)東西埋涧,它沒有思維跳躍得那么快...
    嘰哩咕嚕AMY貓閱讀 338評(píng)論 4 4
  • 濃香一脈勝春華, 晶瑩莼桂香萬家. 湖心偶有扁舟至, 迤儷情懷最嗟呀. 吾嘆西子風(fēng)情雋, 四時(shí)晴雨不相厭. 來年若...
    君子闕閱讀 244評(píng)論 1 1
  • 1 貨幣政策:貼現(xiàn)率的調(diào)整板辽、銀行法定儲(chǔ)備金率的調(diào)整、國(guó)債棘催。 2 財(cái)政政策:通過(財(cái)政支出)與(稅收政策)的變動(dòng)來影...
    無邊小豬閱讀 371評(píng)論 0 1