UICollectionView之滾動圖片縮放

開始前的準備
  • 先看下效果,這個效果是使用UICollectionView實現的,通過自定義繼承自系統的流水布局
kobe.gif
  • 如果你對上面效果感興趣,那非常歡迎你繼續往下看,首先我需要先說明幾個關于自定義繼承自系統的流水布局的幾個關鍵方法,實現這個效果的基礎就是先清楚這幾個方法哦
    實現這個效果,就默認大家已經掌握了collectionView的最基本使用了哦,所以這里就不在詳細說明collectionView的一些基本屬性和方法啦
基本結構

實現該效果其實也不是很復雜的哦

  • 創建一個UICollectionView,尺寸和屏幕一樣大
  • 自定義Cell繼承自UICollectionViewCell --- LBPhotoCell
  • 自定義布局,繼承自系統的UICollectionViewFlowLayout --- LBVerLayout
關鍵方法說明

可能會枯燥,但是這個是理解這個效果的前提哦,我也會非常用心的講解

1)重寫   - (void)prepareLayout

 - 該方法是準備布局,會在cell顯示之前調用,可以在該方法中設置布局的一些屬性,比如滾動方向,cell之間的水平間距,以及行間距等
 - 也建議在這個方法中做布局的初始化操作,不建議在init方法中初始化,這個時候可能CollectionView還沒有創建,官方文檔也有明確說明哦
 - 如果重寫了該方法,一定要調用父類的prepareLayout
2) 重寫   - (NSArray *)layoutAttributesForElementsInRect:(CGRect):rect
 
 - 該方法的返回值是一個存放著rect范圍內所有元素的布局屬性的數組
 - 數組里面的對象決定了rect范圍內所有元素的排布(frame)
 - 里面存放的都是UICollectionViewLayoutAttributes對象,該對象決定了cell的排布樣式
 - 一個cell就對應一個UICollectionViewLayoutAttributes對象
 - UICollectionViewLayoutAttributes對象決定了cell的frame
3) 重寫   - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
  - 是否允許在里面cell位置改變的時候重新布局
  - 默認是NO,返回YES的話,該方法內部重新會按順序調用以下2個方法
      **- (void)prepareLayout
      **- (NSArray *)layoutAttributesForElementsInRect:(CGRect):rect

4)重寫   - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
 - proposedContentOffset:原本情況下,collectionview停止滾動時最終的偏移量
    **滑動的時候手松開因為慣性并不會立即停止,會再滾動一會才會真正停止,這個屬性就是記錄這個真正停止這一刻的偏移量
    **我們這個效果是手指松開,完全停止滾動的時候,離屏幕中間y值最近的cell自動滾動到屏幕的中間
    **所以我們需要利用該方法的返回值,這個返回值就是需要我們給一個偏移量,這個collectionview在它由于慣性滾動結束后,再去多滾動我們給的這一部分偏移量
 - velocity:滾動速率,可以根據velocity的x或y判斷它是向上/向下/向右/向左滑動
    **這個參數在這里沒有什么用,但是這個參數本身還是非常有用的,我之前使用過它來判斷當前tabbleview是向上滑還是向下滑,這個時候可以通過這個判斷很簡單的就控制是隱藏tabBar或者顯示tabBar,或者是隱藏顯示導航條,使用很爽
具體實現

復雜的方法說明后,終于迎來了更為枯燥的擼碼時刻~~~
1)在ViewController.m文件中

static NSString * const LBPhoto = @"kobe";

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 創建布局
    LBVerLayout *layout = [[LBVerLayout alloc] init];
    layout.itemSize = CGSizeMake(150, 150);
    
    // 創建collectionView
    CGFloat collectionW = self.view.frame.size.width;
    CGFloat collectionH = self.view.frame.size.height;
    CGRect frame = CGRectMake(0, 0, collectionW , collectionH);
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    collectionView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:collectionView];
    
    // 注冊cell,我這里是使用的xib
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([LBPhotoCell class]) bundle:nil] forCellWithReuseIdentifier:LBPhoto];    

}

#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 20;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{ 
    //創建cell
    LBPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:LBPhoto forIndexPath:indexPath];
    //重寫imageName的set方法,內部其實就是給cell的imageView賦值(imageView是我們自己給cell添加的子控件)
    cell.imageName = [NSString stringWithFormat:@"kobe_%zd", indexPath.item ];
}

2)在繼承自UICollectionViewCell的自定義cell的.m文件中

@interface LBPhotoCell()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation LBPhotoCell

- (void)awakeFromNib {
    //給imageView的圖層設置邊框寬度以及邊框顏色
    self.imageView.layer.borderWidth = 10;
    self.imageView.layer.borderColor = [UIColor blackColor].CGColor;
}

//重寫imageName的set方法,外界傳一個圖片名給我們,我們在cell內部給cell的子控件賦值
- (void)setImageName:(NSString *)imageName
{
    _imageName = [imageName copy];
    self.imageView.image = [UIImage imageNamed:imageName];
}
@end

3)最后一步,最為關鍵的一步,賦值的計算都是在這個類里面實現的
在繼承自系統的UICollectionViewFlowLayout的類的.m文件中

- (void)prepareLayout
{
    [super prepareLayout];
    // 垂直滾動
    self.scrollDirection = UICollectionViewScrollDirectionVertical;
    self.minimumInteritemSpacing = 20;

    // 設置collectionView里面內容的內邊距(上、左、下、右)
    CGFloat inset = (self.collectionView.frame.size.width - 2*self.itemSize.width) /3;
    self.sectionInset = UIEdgeInsetsMake(inset, inset, inset, inset);
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
     // 拿到系統已經幫我們計算好的布局屬性數組,然后對其進行拷貝一份,后續用這個新拷貝的數組去操作
    NSArray * originalArray   = [super layoutAttributesForElementsInRect:rect];
    NSArray * curArray = [[NSArray alloc] initWithArray:originalArray copyItems:YES];
    
    // 計算collectionView中心點的y值(這個中心點可不是屏幕的中線點哦,是整個collectionView的,所以是包含在屏幕之外的偏移量的哦)
    CGFloat centerY = self.collectionView.contentOffset.y + self.collectionView.frame.size.height * 0.5;
    
    // 拿到每一個cell的布局屬性,在原有布局屬性的基礎上,進行調整
    for (UICollectionViewLayoutAttributes *attrs in curArray) {
        // cell的中心點y 和 collectionView最中心點的y值 的間距的絕對值
        CGFloat space = ABS(attrs.center.y - centerY);
        
        // 根據間距值 計算 cell的縮放比例
        // 間距越大,cell離屏幕中心點越遠,那么縮放的scale值就小
        CGFloat scale = 1 - space / self.collectionView.frame.size.height;
        
        // 設置縮放比例
        attrs.transform = CGAffineTransformMakeScale(scale, scale);
    }
    
    return curArray;
}

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    // 計算出停止滾動時(不是松手時)最終顯示的矩形框
    CGRect rect;
    rect.origin.y = proposedContentOffset.y;
    rect.origin.x = 0;
    rect.size = self.collectionView.frame.size;
    
    // 獲得系統已經幫我們計算好的布局屬性數組
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    
    // 計算collectionView最中心點的y值
    // 再啰嗦一下,這個proposedContentOffset是系統幫我們已經計算好的,當我們松手后它慣性完全停止后的偏移量
    CGFloat centerY = proposedContentOffset.y + self.collectionView.frame.size.height * 0.5;
    
    // 當完全停止滾動后,離中點Y值最近的那個cell會通過我們多給出的偏移量回到屏幕最中間
    // 存放最小的間距值
    // 先將間距賦值為最大值,這樣可以保證第一次一定可以進入這個if條件,這樣可以保證一定能鬧到最小間距
    CGFloat minSpace = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attrs in array) {
        if (ABS(minSpace) > ABS(attrs.center.y - centerY)) {
            minSpace = attrs.center.y - centerY;
        }
    }
    // 修改原有的偏移量
    proposedContentOffset.y += minSpace;
    return proposedContentOffset;
}

點擊這里下載代碼

(ps:感謝@予人與人,親自敲了代碼發現了我漏掉的一個問題,非常感謝)

結束語
  • OK,這個效果也基本實現了,忙碌的周末也快結束了,明天公司會有新人過來面試,一些不錯的面試題有機會的話會繼續跟大家分享的
  • 慢慢發現把自己稍微了解的東西分享出去這樣非常有助于自己的提升,感覺自己會不是牛逼的,最牛逼的是把自己會的能分享給別人,過程中會發現很多細節
  • 今后會經常性的和大家分享實用有趣的東西,我們共同提升
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,791評論 6 545
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,795評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,943評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,057評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,773評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,106評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,082評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,282評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,793評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,507評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,741評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,220評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,929評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,325評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,661評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,482評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,702評論 2 380

推薦閱讀更多精彩內容