UICollectionView實(shí)現(xiàn)Gallery滑動(dòng)中間放大效果

上一篇介紹了UICollectionView的基本使用,這篇介紹下如何實(shí)現(xiàn)如Keep勛章的滑動(dòng)自動(dòng)縮放的交互效果,即Gallery畫廊效果哄陶,只需要利用layout即可輕松實(shí)現(xiàn)琳省。


Keep勛章

我們先按上一篇?jiǎng)?chuàng)建UICollectionView以顯示數(shù)據(jù),代碼如下:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.itemSize = CGSize(width: 100, height: 100)
        let collectionView = UICollectionView(frame: CGRectMake(0, 100, view.bounds.width, 150), collectionViewLayout: layout)
        collectionView.dataSource = self
        collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
        view.addSubview(collectionView)
    }
}

extension ViewController: UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 30
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath) as! ItemCell
        cell.imageView.image = UIImage(named: "\(indexPath.row)")
        return cell
    }
}

因?yàn)槲覀冞€是像上一篇一樣用的UICollectionViewFlowLayout嗤形,運(yùn)行后精偿,目前只是簡單的平鋪效果:


Simulator Screenshot - iPhone 15 - 2023-12-20 at 17.47.47.png

接下來我們要替換layout為我們自己創(chuàng)建的UICollectionViewLayout子類。因?yàn)榭傮w上我們還是網(wǎng)格布局赋兵,可以直接繼承自UICollectionViewFlowLayout笔咽,可節(jié)省大量代碼:


創(chuàng)建LineLayout

先上完整代碼,再解釋

import UIKit

class LineLayout: UICollectionViewFlowLayout {
    let ActiveDistance = 80.0;
    let ScaleFactor = 1.0;
    var seletedIndex = 0
    
    override init() {
        super.init()
        scrollDirection = .horizontal
        minimumLineSpacing = 20
        itemSize = CGSize(width: 110, height: 110)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    /// 調(diào)整選中的cell居中對(duì)齊
    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        //proposedContentOffset是沒有設(shè)置對(duì)齊時(shí)本應(yīng)該停下的位置
        guard let collectionView = collectionView else {return proposedContentOffset}

        //targetRect是collectionView當(dāng)前顯示在屏幕的區(qū)域霹期,array保存此區(qū)域內(nèi)的所有cell布局信息(UICollectionViewLayoutAttributes)
        let collectionW = collectionView.bounds.width
        let collectionH = collectionView.bounds.height
        var offsetAdjustment = 10000.0
        let horizontalCenter = proposedContentOffset.x + collectionW / 2
        let targetRect = CGRect(x: proposedContentOffset.x, y: 0.0, width: collectionW, height: collectionH)
        guard let array = super.layoutAttributesForElements(in: targetRect) else {return proposedContentOffset}

        //對(duì)當(dāng)前屏幕中的UICollectionViewLayoutAttributes逐個(gè)與屏幕中心進(jìn)行比較叶组,找出最接近中心的一個(gè)
        var itemHorizontalCenter = 0.0
        for layoutAttributes in array {
            itemHorizontalCenter = layoutAttributes.center.x
            if (abs(itemHorizontalCenter - horizontalCenter) < abs(offsetAdjustment)) {
                offsetAdjustment = itemHorizontalCenter - horizontalCenter
            }
        }

        //返回調(diào)整后的偏移位置
        return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y
        )
    }

    /** 有效距離:當(dāng)item的中間x距離屏幕的中間x在ActiveDistance以內(nèi),開始放大, 其它情況都是縮小 */
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let array = super.layoutAttributesForElements(in: rect)

        guard let collectionView = collectionView,let array = array else {return array}
        let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
        // 遍歷布局屬性
        for attribute in array {
            //如果cell在屏幕上則進(jìn)行縮放處理
            if CGRectIntersectsRect(attribute.frame, rect) {
                // 距離中點(diǎn)的距離
                let distance = CGRectGetMidX(visibleRect) - attribute.center.x
                let normalizedDistance = distance / ActiveDistance
                if (abs(distance) < ActiveDistance) {
                    let zoom = 1 + ScaleFactor * (1 - abs(normalizedDistance))
                    attribute.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0)
                    attribute.zIndex = 1
                    attribute.alpha = max(1 - normalizedDistance, 0.6)
                    seletedIndex = attribute.indexPath.row
                }else {
                    attribute.alpha = 0.6
                }
            }
        }
        return array
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        true
    }
    
}

1、init方法

在init方法中历造,我們?cè)O(shè)置了collectionView的滑動(dòng)方向甩十、cell間距、cell的大小

2吭产、實(shí)現(xiàn)滑動(dòng)后cell自動(dòng)居中

通過重寫如下方法:

func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint

其中proposedContentOffset參數(shù)為滑動(dòng)結(jié)束后侣监,collectionView最終的偏移位置,返回值為我們期待的偏移位置臣淤。即我們只要通過本該偏移的位置橄霉,通過計(jì)算找到此時(shí)離屏幕中點(diǎn)最近的cell,重新計(jì)算偏移位置并返回邑蒋,就可以讓最靠近中點(diǎn)的cell完全居中了姓蜂。

3、中間的cell放大效果

通過重寫以下方法:

func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?

參數(shù)rect為當(dāng)前需要展示的所有cell所在collectionview的區(qū)域寺董,我們只需要獲取到這區(qū)域內(nèi)的所有cell的布局信息覆糟,并做對(duì)應(yīng)的縮放、透明度的修改后遮咖,作為返回值返回即可滩字。

最后,將原來的UICollectionViewFlowLayout替換為LineLayout即可:

override func viewDidLoad() {
       super.viewDidLoad()
       
       let layout = LineLayout()
       let collectionView = UICollectionView(frame: CGRectMake(0, 100, view.bounds.width, 150), collectionViewLayout: layout)
       collectionView.dataSource = self
       collectionView.register(UINib(nibName: "ItemCell", bundle: nil), forCellWithReuseIdentifier: "ItemCell")
       view.addSubview(collectionView)
   }

運(yùn)行后效果如下:


最終效果
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末御吞,一起剝皮案震驚了整個(gè)濱河市麦箍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陶珠,老刑警劉巖挟裂,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異揍诽,居然都是意外死亡诀蓉,警方通過查閱死者的電腦和手機(jī)栗竖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渠啤,“玉大人狐肢,你說我怎么就攤上這事×げ埽” “怎么了份名?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妓美。 經(jīng)常有香客問我僵腺,道長,這世上最難降的妖魔是什么壶栋? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任辰如,我火速辦了婚禮,結(jié)果婚禮上委刘,老公的妹妹穿的比我還像新娘丧没。我一直安慰自己,他們只是感情好锡移,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布呕童。 她就那樣靜靜地躺著,像睡著了一般淆珊。 火紅的嫁衣襯著肌膚如雪夺饲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天施符,我揣著相機(jī)與錄音往声,去河邊找鬼。 笑死戳吝,一個(gè)胖子當(dāng)著我的面吹牛浩销,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播听哭,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼慢洋,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了陆盘?” 一聲冷哼從身側(cè)響起普筹,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隘马,沒想到半個(gè)月后太防,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酸员,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年蜒车,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了讳嘱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡醇王,死狀恐怖呢燥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寓娩,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布呼渣,位于F島的核電站棘伴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏屁置。R本人自食惡果不足惜焊夸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蓝角。 院中可真熱鬧阱穗,春花似錦、人聲如沸使鹅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽患朱。三九已至鲁僚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間裁厅,已是汗流浹背冰沙。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留执虹,地道東北人拓挥。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像袋励,于是被迫代替她去往敵國和親侥啤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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