天天看點

iOS CollectionView 清單&網格之間切換(帶動畫)

iOS CollectionView 清單&網格之間切換(帶動畫)

原文位址:https://www.hlzhy.com/?p=57

前言:

最近在寫一個清單界面,這個清單能夠在清單和網格之間切換,這種需求算是比較常見的。本以為想我們是站在大牛的肩膀上程式設計,就去找了下度娘和谷哥,但是并沒有找到我想要的(找到的都是不帶動畫的切換)。既然做不了VC戰士,那就自己動手豐衣足食。在我看來,所有的視圖變化都應該至少帶個簡單的過渡動畫,當然,過度使用華麗的動畫效果也會造成使用者的審美疲勞。“動畫有風險,使用需謹慎”。

依稀記得以前面試的時候被面試官問過這個問題,并被告知CollectionView自帶有清單和網格之間切換并且帶動畫的API。最終找到如下方法:

/**
Summary
Changes the collection view’s layout and optionally animates the change.

Discussion
This method makes the layout change without further interaction from the user. If you choose to animate the layout change, the animation timing and parameters are controlled by the collection view.
*/
- (void)setCollectionViewLayout:(UICollectionViewLayout *)layout animated:(BOOL)animated; // transition from one layout to another

           
iOS CollectionView 清單&網格之間切換(帶動畫)

最終效果(切換動畫&動畫慢放).gif

實作:

UIViewController.m

一、初始化UICollectionView

在目前控制器準備一個

BOOL

isList

,用來記錄目前選擇的是清單還是網格,準備兩個

UICollectionViewFlowLayout

對應清單和網格的布局,設定一個

NOTIFIC_N_NAME

宏,将此宏作為NotificationName,稍後将以通知的方式通知Cell改變布局。并且初始化UICollectionView。

@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource>
@property (nonatomic, strong) UICollectionView *myCollectionView;

@property (nonatomic, assign) BOOL isList;
@property (nonatomic, strong) UICollectionViewFlowLayout *gridLayout;
@property (nonatomic, strong) UICollectionViewFlowLayout *listLayout;
@end
#define NOTIFIC_N_NAME @"ViewController_changeList"
@implementation ViewController
-(UICollectionViewFlowLayout *)gridLayout{
    if (!_gridLayout) {
        _gridLayout = [[UICollectionViewFlowLayout alloc] init];
        CGFloat width = (self.view.frame.size.width - 5) * 0.5;
        _gridLayout.itemSize = CGSizeMake(width, 200 + width);
        _gridLayout.minimumLineSpacing = 5;
        _gridLayout.minimumInteritemSpacing = 5;
        _gridLayout.sectionInset = UIEdgeInsetsZero;
    }
    return _gridLayout;
}
-(UICollectionViewFlowLayout *)listLayout{
    if (!_listLayout) {
        _listLayout = [[UICollectionViewFlowLayout alloc] init];
        _listLayout.itemSize = CGSizeMake(self.view.frame.size.width, 190);
        _listLayout.minimumLineSpacing = 0.5;
        _listLayout.sectionInset = UIEdgeInsetsZero;
    }
    return _listLayout;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    _myCollectionView = [[UICollectionView alloc]initWithFrame:self.view.bounds collectionViewLayout:self.gridLayout];
    _myCollectionView.showsVerticalScrollIndicator = NO;
    _myCollectionView.backgroundColor = [UIColor grayColor];
    _myCollectionView.delegate = self;
    _myCollectionView.dataSource = self;
    [self.view addSubview:_myCollectionView];
    [self.myCollectionView registerClass:[HYChangeableCell class] forCellWithReuseIdentifier:@"HYChangeableCell"];
    //......
}
           

二、實作UICollectionViewDataSource

建立UICollectionViewCell,給

cell.isList

指派, 告訴Cell目前狀态,給

cell.notificationName

指派,用以接收切換通知。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    HYChangeableCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"HYChangeableCell" forIndexPath:indexPath];
    cell.isList = _isList;
    cell.notificationName = NOTIFIC_N_NAME;
    return cell;
}
           

三、點選切換按鈕

通過

setCollectionViewLayout:animated:

方法重新為CollectionView布局,并将

animated

設為YES。但是僅僅這樣是不夠的,因為這樣并不會觸發

cellForItemAtIndexPath

方法。我們還需向Cell發送通知告訴它“你需要改變布局了”。

-(void)changeListButtonClick{
    _isList = !_isList;
    if (_isList) {
        [self.myCollectionView setCollectionViewLayout:self.listLayout animated:YES];
    }else{
        [self.myCollectionView setCollectionViewLayout:self.gridLayout animated:YES];
    }
    //[self.myCollectionView reloadData];
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFIC_N_NAME object:@(_isList)];
}
           

UICollectionViewCell.m

基本布局代碼這裡就不貼上來了,需要的請在文章最後自行下載下傳Demo檢視。

!注意:因為這裡使用的是UIView動畫,因為UIView動畫并不會根據我們肉眼所看到的動畫效果過程中來動态改變寬高,在動畫開始時其寬高就已經是結束狀态時的寬高。是以用Masonry給子視圖布局時,限制對象盡可能的避免Cell的右邊和底邊。否則動畫将會出現異常,如下圖的TitleLabel,我們能看到在切換時title寬度是直接變短的,也造成其它Label以它為限制對象時動畫異常(下面紅色字型的Label,切換時會往下移位)。

iOS CollectionView 清單&amp;網格之間切換(帶動畫)

title限制為右邊時動畫慢放.gif

一、重寫layoutSubviews

通過重寫layoutSubviews方法,将

[super layoutSubviews]

寫進UIView動畫中,使Cell的切換過渡動畫更平滑。

-(void)layoutSubviews{
    [UIView animateWithDuration:0.3 animations:^{
        [super layoutSubviews];
    }];
}
           

二、重寫setNotificationName

重寫setNotificationName方法并注冊觀察者。實作通知方法,将通知傳來的值指派給

isList

最後記得移除觀察者!

-(void)setNotificationName:(NSString *)notificationName{
    if ([_notificationName isEqualToString:notificationName]) return;
    _notificationName = notificationName;
    //注冊通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(isListChange:) name:_notificationName object:nil];
}

-(void)isListChange:(NSNotification *)noti{
    BOOL isList = [[noti object] boolValue];
    [self setIsList:isList];
}

-(void)dealloc{
    //移除觀察者
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
           

三、重寫setIsList

重寫setIsList方法,通過判斷

isList

值改變子視圖的布局。

代碼較多,詳細代碼請下載下傳Demo檢視。

  • 此方法内 接收到通知進入時cell的frame并不準确,此時如果需要用到self.width,則需要自行計算,例如:
-(void)setIsList:(BOOL)isList{
    if (_isList == isList) return;
    _isList = isList;
    CGFloat width = _isList ? SCREEN_WIDTH : (SCREEN_WIDTH - 5) * 0.5;
    if (_isList) {
       //......
    }else{
       //......
    }
    //......
           
  • 如使用Masonry

    當布局相對簡單時,限制使用mas_updateConstraints進行更新即可。當布局比較複雜,限制涉及到某控件寬,而這控件寬又是不固定的時候,可以考慮使用mas_remakeConstraints重做限制。

  • 限制都設定完成後,最後調用UIView動畫更新限制。如果有用frame設定的,也将設定frame代碼寫在UIView動畫内。

    !注意:如有用masonry限制關聯了 用frame設定的視圖,則此處需要把frame設定的視圖寫在前面。

-(void)setIsList:(BOOL)isList{
    //......
    [UIView animateWithDuration:0.3f animations:^{
        self.label3.frame = frame3;
        self.label4.frame = frame4;
        
        [self.contentView layoutIfNeeded];
    }];
}
           

Demo:

HYChangeableCollection

-END-

如果此文章對你有幫助,希望給個❤️。有什麼問題歡迎在評論區探讨。

作者:Hank_Zhong

連結:https://www.jianshu.com/p/443b73f72702

來源:簡書

簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

繼續閱讀