1. 程式人生 > >UICollectionView 的研究之二 :自定義 UICollectionViewFlowLayout

UICollectionView 的研究之二 :自定義 UICollectionViewFlowLayout

UICollectionView 實現各式複雜佈局核心在於 UICollectionViewLayout,需要我們去自定義實現。

通過各種layout 的自定義實現,以及它們之間的切換。可以實現一些酷炫的佈局,例如

Cover Flow 佈局

堆疊佈局


圓形佈局


關於需要重寫方法的描述

自定義佈局需要重寫以下四個方法

 - 作用:在這個方法中做一些初始化操作

 - 注意:子類重寫prepareLayout,一定要呼叫[super prepareLayout]

 - (void)prepareLayout;

 - 作用:

 - 這個方法的返回值是個陣列

 - 這個陣列中存放的都是UICollectionViewLayoutAttributes物件

 - UICollectionViewLayoutAttributes物件決定了cell的排布方式(frame等)

- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;

 - 作用:如果返回YES,那麼collectionView顯示的範圍發生改變時,就會重新重新整理佈局

 - 一旦重新重新整理佈局,就會按順序呼叫下面的方法:

 - prepareLayout

 - layoutAttributesForElementsInRect:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;

- 作用:返回值決定了collectionView停止滾動時最終的偏移量(contentOffset)

 - 引數:

 - proposedContentOffset:原本情況下,collectionView停止滾動時最終的偏移量

 - velocity:滾動速率,通過這個引數可以瞭解滾動的方向

-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint

)velocity;

具體實現邏輯

以 Cover Flow 佈局的實現為例子進行闡述


進行初始化

- (instancetype)init {
    if (self = [super init]) {
        
    }
    return self;
}

進行佈局,並且 collectionView 顯示 rect 改變是進行佈局重新整理

- (void)prepareLayout {
    [super prepareLayout];
    
    // 水平滾動
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    
    // 決定第一張圖片所在的位置
    CGFloat margin = (self.collectionView.frame.size.width - self.itemSize.width) / 2;
    self.collectionView.contentInset = UIEdgeInsetsMake(0, margin, 0, margin);
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
} // return YES to cause the collection view to requery the layout for geometry information

該方法是進行自定義的核心,在這裡寫各種演算法以達成想要的效果

該例子中,我們要實現的效果是在滑動的過程中,item 逐漸接近中心,會逐步增大,直到顯示最大值,之後不斷遠離中心點,item 逐步縮小。這一效果類似於三角函式中的正、餘弦函式。初步鎖定這兩個函式,而要選擇哪一個?

在滑動的過程中,我們可以得到

self.collectionView.contentOffset.x :這是內容最左側相對於 collectionView 最左側的偏移值

attributes.center.x :這是當前 item 的水平中心點,該 x 值是從內容的最左側算起,直到當前 item 的水平中心點的,全部加起來就是該 item 的水平中心 x。

再者,無論是否滑動,都有一個固定值:

self.collectionView.center.x :位於螢幕水平方向的中心不變,大小不變。


那麼,以第一個item 進行分析,self.collectionView.contentOffset.x與 self.collectionView.contentOffset.的差值與螢幕中心 x 相減的絕對值為item 中心與螢幕中心之間的距離

    

              螢幕中心與item 中心相距為0                                 螢幕中心與item 中心相距40

由此,我們選擇餘弦函式進行計算。

- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    
    // 閃屏現象解決參考 :https://blog.csdn.net/u013282507/article/details/53103816
    //擴大控制範圍,防止出現閃屏現象
    rect.size.width = rect.size.width + KScreenWidth;
    rect.origin.x = rect.origin.x - KScreenWidth/2;

    // 讓父類佈局好樣式
    NSArray *arr = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
    
    
    for (UICollectionViewLayoutAttributes *attributes in arr) {
        CGFloat scale;
//        scale = 1.0;
        
        // collectionView 的 centerX
        CGFloat centerX = self.collectionView.center.x;
        CGFloat step = ABS(centerX - (attributes.center.x - self.collectionView.contentOffset.x));
        NSLog(@"step %@ : attX %@ - offset %@", @(step), @(attributes.center.x), @(self.collectionView.contentOffset.x));
        scale = fabsf(cosf(step/centerX * M_PI/5));
        
        attributes.transform = CGAffineTransformMakeScale(scale, scale);
    }
    
    return arr;
} // return an array layout attributes instances for all the views in the given rect

確保在滾動結束的時候的顯示效果,此處確保某一個item 滾動結束時是居中顯示的。

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
    
    // 保證滾動結束後檢視的顯示效果
    
    // 計算出最終顯示的矩形框
    CGRect rect;
    rect.origin.y = 0;
    rect.origin.x = proposedContentOffset.x;
    rect.size = self.collectionView.frame.size;

    // 獲得 super 已經計算好的佈局的屬性
    NSArray *arr = [super layoutAttributesForElementsInRect:rect];

    // 計算 collectionView 最中心點的 x 值
    CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;

    CGFloat minDelta = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attrs in arr) {
        if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
            minDelta = attrs.center.x - centerX;
        }
    }

    proposedContentOffset.x += minDelta;
    
    return proposedContentOffset;
} // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior

所有的參考連結:

WWDC 2012 Session 筆記 -- 205 Introducing Collection Views

UICollectionViewLayout 繼承 UICollectionViewFlowLayout 自定義佈局

自定義流水佈局(UICollectionViewFlowLayout 的基本使用)

iOS 利用餘弦函式實現卡片瀏覽工具


相關推薦

UICollectionView研究 定義 UICollectionViewFlowLayout

UICollectionView 實現各式複雜佈局核心在於 UICollectionViewLayout,需要我們去自定義實現。通過各種layout 的自定義實現,以及它們之間的切換。可以實現一些酷炫的佈局,例如Cover Flow 佈局堆疊佈局圓形佈局關於需要重寫方法的描述

Ext4.2.1學習歷程定義類及類的動態載入

原文出處   http://blog.itpub.net/28562677/viewspace-1067421/ -------------------------------------------------------------- 在些extjs類的定義時有必要簡單

物聯網安全研究IoT系統攻擊面定義分析

在前文中,我們瞭解了IoT技術的基本架構,本文我將來說說IoT安全,在此過程中,我們會嘗試定義一種新方法來理解IoT安全,同時也會建立一個結構化流程來方便認知IoT相關的攻擊研究和滲透測試。 依據前文我們定義的IoT體系結構,現在我們可以非常清晰地分離出物聯網系統的各種

Zabbix()定義腳本、遠程命令、報警升級測試實例

zabbix1.自定義腳本Zabbix中有統一的報警腳本存放路徑,即:/usr/lib/zabbix/alertscripts。[root@zrs1 ~]# cd /usr/lib/zabbix/alertscripts/ [root@zrs1 alertscripts]# vim mailalert.sh

Oracle Spatial分區應用研究按縣分區與按省分區對比測試報告

oracle 出了 ali 明顯 基礎上 實驗方法 樣本 空間查詢 使用場景 1、實驗目的 在上一輪的實驗中,oracle 11g r2版本下,在87縣市實驗數據的基礎上,比較了分表與分區的效率,得出了分區+全局索引效率較高的結論(見上一篇博客)。不過我們尚未比較過

如何安裝和配置打印服務器定義客戶端電腦使用網絡打印機的默認設置

oss strong mode 客戶端 pre 51cto col str 裝配 如何安裝和配置打印服務器之六:自定義客戶端電腦使用網絡打印機的默認設置 ?Lander Zhang 專註外企按需IT基礎架構運維服務,IT Helpdesk 實戰培訓踐行者http://blo

spring security 5.x 使用及分析(定義配置—初階)

二、自定義配置(初階): 自定義的配置,就要修改一些預設配置的資訊,從那開始入手呢? 1、第一步:建立Spring Security 的Java配置,改配置建立一個名為springSecurityFilterChain的servlet過濾器,它負責應用程式的安

sysbench花式採坑增值導致的主鍵衝突

上期《sysbench花式採坑之一:自增值導致的TPS不可靠》介紹到,在sysbench壓測過程中,如果自增值不為1會導致效能測試值偏高的現象,其實在發現這個現象之前,在單例項效能測試時我還遇到了一個主鍵衝突的問題。 | MySQL單例項sysbench壓測時出現主鍵衝突 《sys

Netty4.0學習筆記系列定義通訊協議

實現的原理是通過Encoder把java物件轉換成ByteBuf流進行傳輸,通過Decoder把ByteBuf轉換成java物件進行處理,處理邏輯如下圖所示: 傳輸的Java bean為Person: package com.guowl.testobjcoder

彈窗定義Dialog

第一步: 給Dialog設定一個風格主題 (基本都是用這個主題)無邊框全透明背景: res/values/styles: <!--自定義dialog背景全透明無邊框theme --> <style name="MyDi

Android構建一個通用的WebView()定義的錯誤頁面、快取資料,離線瀏覽

概述 12.24追加的WebView功能包括: 1.支援載入網頁失敗時載入自定義的錯誤頁面 2.支援快取網頁資料,提供離線瀏覽 效果     介紹 WebView本身已自帶了快取功能,當首次載入網頁時會在/data/data/package_name目錄下生成databa

android中的對話方塊定義對話方塊

首先看下效果圖 下面講一下具體的實現: 1.修改系統預設的Dialog樣式(風格、主題) 2.自定義Dialog佈局檔案 3.可以自己封裝一個類,繼承自Dialog或者直接使用Dialog類來實現,為了方便以後重複使用,建議自己封裝一個Dialog類 ==

【APACHE MINA2.0開發定義實現SERVER/CLIENT端的編解碼工廠(定義編碼與解碼器)!

在上一篇博文中已經簡單介紹過“過濾器”的概念,那麼在Mina 中的協議編解碼器通過過濾器 ProtocolCodecFilter 構造,這個過濾器的構造方法需 要一個 ProtocolCodecFactory,這從前面註冊 TextLineCodecFactory 的程式碼就可以看出來。 Protoc

Android定義控制元件系列定義開關按鈕(一)

這一次我們將會實現一個完整純粹的自定義控制元件,而不是像之前的組合控制元件一樣,拿系統的控制元件來實現;計劃分為三部分:自定義控制元件的基本部分,和自定義控制元件的自定義屬性; 下面就開始第一部分的編寫,本次以一個定義的開關按鈕為例,下面就開始吧: 先看看效果,一個點選開

Wordpress__定義編輯器按鈕

做一個很簡單的功能 框架上大概就是新增一個快捷鍵 編輯文章的時候,可以在文章頭加上鍊接,例如:  =================== 明日花->2015  bababalabala ,bala, =================== 首先想到的當然是- 1.wo

Flink的sink實戰定義

### 歡迎訪問我的GitHub [https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) 內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等; ###

定義spring boot starter三部曲實戰開發

本文是《自定義spring boot starter三部曲》的第二篇,上一篇中我們通過學習spring cloud的starter,對spring boot的starter有了初步瞭解,也設計好了實戰內容,今天就來一起實現; 三部曲文章連結 《自定義spring boot

swift詳解十三------------UICollectionView基礎用法和簡單定義

UICollectionView基礎用法和簡單自定義 注:本文通過幾個例項來講講UICollectionView基本用法 本次要實現的兩個效果。感謝貓神提供的教程 OneV’s Den 第一個介面是一個普通的流佈局 UICollectionView

LIVE555研究RTPServer(

tpch live555 循環調用 family 每一個 函數 計算 ack close LIVE555研究之五:RTPServer(二) 接上文,main函數的幾行代碼創建了RTSPSe

實現類似微信表情包橫向滾動翻頁的功能,運用UICollectionView定義UICollectionViewFlowLayout,cell左右排版 ,支持多組Cell實現。

hang sig idt 滾動翻頁 功能 details assign 實現類 targe 結合:https://blog.csdn.net/qiuhaozhou/article/details/54582741 下面是我所要的樣式的實現的代碼: .h文件如下: #i