Reprinted in author: Hank_Zhong Address: www.hlzhy.com/?p=57

Preface:

These days it’s common to write a list interface that can switch between a list and a grid. I thought we were standing on the shoulder of Daniel programming, so I went to Baidu and Gu Ge, but I did not find what I want (what I found is not to drive the painting switch). If you can’t be a VC warrior, you can feed yourself. In my opinion, all view changes should be animated with a simple transition as possible. Of course, overuse of gorgeous animations can lead to user fatigue. Animation is risky and should be used with caution.

I remember being asked this question in an interview and being told that the CollectionView has an API for switching between lists and grids and driving the drawing. Finally found the following method:

/** Summary Changes the collection view's layout and optionally animates the change. Discussion This method makes the optionally 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

Copy the code

Implementation:

UIViewController.m

Initialize UICollectionView

In preparing a BOOL value isList current controller, is used to record the currently selected list or grid, ready to two UICollectionViewFlowLayout corresponding to the list and the grid layout, setting up a NOTIFIC_N_NAME macros, Use this macro as a NotificationName, which notifies the Cell later to change the layout. And initialize the 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. The size. The width to 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"]; / /... }Copy the code

Second, the implementation UICollectionViewDataSource

Create a UICollectionViewCell, assign a value to cell.isList to tell the cell the current state, and assign a value to cell.notificationName to receive switch notifications.

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

Three, click the switch button

Through setCollectionViewLayout: animated: method for CollectionView layout, and animated set to YES. But this is not enough because it does not trigger the cellForItemAtIndexPath method. We also need to send a notification to the Cell telling it, “You need to change the layout.”

-(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)];
}
Copy the code

UICollectionViewCell.m

The basic layout code is not posted here, please download the Demo at the end of the article. ! Note: since UIView animation is used here, since UIView animation does not dynamically change its width and height based on what we see during the animation, the width and height at the beginning of the animation is already the width and height at the end of the animation. Therefore, when arranging the navigation sub-view, the constraint object should avoid the right and bottom edges of the Cell as much as possible. Otherwise, the animation will be abnormal, as shown in the TitleLabel below. We can see that the title width is directly shortened when switching, which also causes the animation to be abnormal when other labels use it as a constraint object (the Label in the red font below will be shifted downward when switching).

Rewrite layoutSubviews

By rewriting the layoutSubviews method, write [super layoutSubviews] into UIView animation to make the transition animation of Cell switch smoother.

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

Rewrite setNotificationName

Override the setNotificationName method and register the observer. Implement a notification method that assigns the value passed by the notification to isList. Finally, remove the observer!

-(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]; [selfsetIsList:isList]; } -(void)dealloc{// removeObserver [[NSNotificationCenter defaultCenter] removeObserver:self]; }Copy the code

Rewrite setIsList

Override the setIsList method to change the layout of the subview by judging the isList value. More code, detailed code please download Demo to view.

  • In this method, the frame of the cell is not accurate when the notification is received. In this case, if self.width is needed, you need to calculate it by yourself. For example:
-(void)setIsList:(BOOL)isList{
    if (_isList == isList) return; _isList = isList; CGFloat width = _isList ? SCREEN_WIDTH: (SCREEN_WIDTH - 5) * 0.5;if (_isList) {
       //......
    }else{/ /... } / /...Copy the code
  • When the layout is relatively simple, for example, use mas_updateConstraints to update it. When the layout is complex and the constraint involves a control width that is not fixed, consider using mas_remakeConstraints to redo the constraint.

  • After all the constraints are set, the UIView animation is called to update the constraints. If you have a frame set, put the frame set code inside the UIView animation. ! Note: If a frame view is associated with the navigation constraint, the frame view should be placed first.

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

Demo:

HYChangeableCollection

Xiaobian this, to recommend you an excellent iOS communication platform, platform partners are very excellent iOS developers, we focus on the sharing of technology and skills exchange, we can discuss technology on the platform, exchange and learning. Welcome to join (if you want to join, you can add small series wechat 15673450590).

-END- If this article is helpful to you, please send it to ❤️. Feel free to discuss any questions in the comments section.