OCiOS开发:集合视图 UICollectionView

简介

UICollectionView 和 UICollectionViewController 类是iOS6 新引进的API,用于展示集合视图,布局更加灵活,可实现多列布局,用法类似于UITableView 和 UITableViewController 类。

使用UICollectionView 必须实现UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout这三个协议。

UICollectionViewFlowLayout

UICollectionViewFlowLayout是苹果内置的一种流式的布局,其继承于UICollectionViewLayout,负责整个视图的布局,通过这个类,可以高度的定制每个item的位置,以及表头、脚注和装饰视图的位置,只需简单的配置几个属性就可以完成整个布局的搭建。

初始化方法

alloc + init

常用属性

  • itemSize:设置item尺寸

  • minimumLineSpacing:设置每行之间的间距

  • minimumInteritemSpacing:设置每行内部item之间的间距

  • headerReferenceSize:设置头部size

  • footerReferenceSize:设置脚注size

  • scrollDirection:设置滚动方向

  • sectionHeadersPinToVisibleBounds:设置是否当元素超出屏幕之后固定头部视图位置,默认NO

  • sectionFootersPinToVisibleBounds:设置是否当元素超出屏幕之后固定尾部视图位置,默认NO

属性配置UICollectionViewFlowLayout是在全局配置布局,想要单独定制每个item元素,需遵守UICollectionViewDelegate以及UICollectionViewDelegateFlowLayout协议,并根据需要实现如下方法即可:

// 1、单独定制item的尺寸
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;

// 2、定义每个UICollectionView的margin(间距),对每一个section单独设置边界,即内部cell上下左右距离header和footer的边界(间距)
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;

// 3、单独定制每行之间的间距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;

// 4、单独定制每行item之间的间距
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;

// 5、单独定制头部视图size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;

// 6、单独定制脚注视图size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;

UICollectionView

初始化

- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; 

常用属性

  • delegate:设置代理

  • dataSource:设置数据源

  • allowsSelection:设置是否可以选中

  • allowsMultipleSelection:设置是否可以多选

注册

  • 注册Cell
- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
  • 注册头部或尾部视图
// @param elementKind
// UICollectionElementKindSectionHeader:头部
// UICollectionElementKindSectionFooter:尾部

- (void)registerClass:(Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;

重用

// 1、Cell重用
- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;

// 2、supplementary View重用
- (id)dequeueReusableSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath*)indexPath;

操作

  • items操作
// 插入
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths;

// 删除
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths;

// 刷新
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths;

// 移动
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
  • sections操作
// 插入
- (void)insertSections:(NSIndexSet *)sections;

// 删除
- (void)deleteSections:(NSIndexSet *)sections;

// 刷新
- (void)reloadSections:(NSIndexSet *)sections;

// 移动
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;

indexPath 获取方法

// 根据某一个点的位置获取item的indexPath
- (nullable NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point;

// 根据cell获取item的indexPath
- (nullable NSIndexPath *)indexPathForCell:(UICollectionViewCell *)cell;

reordering 重新排序

  • 一般重新排序步骤需经历如下周期,与手势搭配使用,根据手势的周期变化执行不同的方法:
// 1、设置是否允许移动item
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath;

// 2、一般在手势开始时调用,与 indexPathForItemAtPoint 搭配使用,根据手势在视图上的位置获取item所在的indexPath, 开始在特定的索引路径上对cell(单元)进行Interactive Movement(交互式移动工作)
- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath;

// 3、 在手势作用期间更新交互移动的目标位置。
- (void)updateInteractiveMovementTargetPosition:(CGPoint)targetPosition;

// 4、在完成手势动作后,结束交互式移动
- (void)endInteractiveMovement;

// 5、取消移动
- (void)cancelInteractiveMovement;

// 6、当移动结束后,如下方法将会被触发。
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath;

UIScrollViewDelegate

// 1、设置是否允许选中
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;

// 2、设置是否允许取消选中
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;

// 3、选中
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;

// 4、取消选中
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;

UICollectionViewDataSource

// 1、设置section组数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;

// 2、设置item个数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;

// 3、配置cell
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;

// 4、自定义头部、脚注视图
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;

tips:配置Cell以及头部或者脚注视图需子类化UICollectionViewCellUICollectionReusableView,并重写initWithFrame方法,添加视图;

案例

效果展示

这里写图片描述

代码示例

自定义 cell 代码示例
#import <UIKit/UIKit.h>

@interface CustomCollectionViewCell : UICollectionViewCell

@property (nonatomic, strong) UILabel *displayLabel; /**< 展示视图 */

@end
#import "CustomCollectionViewCell.h"

@implementation CustomCollectionViewCell

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {

        self.layer.borderWidth   = 1.0f;
        self.layer.borderColor   = [UIColor blackColor].CGColor;
        self.layer.masksToBounds = YES;

        [self.contentView addSubview:self.displayLabel];

    }
    return self;
}

#pragma mark *** Getters ***
- (UILabel *)displayLabel {
    if (!_displayLabel) {
        _displayLabel = [[UILabel alloc] initWithFrame:self.bounds];
        _displayLabel.textAlignment = NSTextAlignmentCenter;
    }
    return _displayLabel;
}

@end
自定义 ReusableView(头部、尾部) 代码示例
#import <UIKit/UIKit.h>

// 自定义头部、尾部视图

@interface CustomCollectionReusableView : UICollectionReusableView

@property (nonatomic, strong) UILabel *displayLabel; /**< 展示标签 */

@end
#import "CustomCollectionReusableView.h"

@implementation CustomCollectionReusableView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubview:self.displayLabel];
    }
    return self;
}

#pragma mark *** Getters ***
- (UILabel *)displayLabel {
    if (!_displayLabel) {
        _displayLabel = [[UILabel alloc] initWithFrame:self.bounds];
        _displayLabel.backgroundColor = [UIColor redColor];
        _displayLabel.textColor = [UIColor whiteColor];
        _displayLabel.textAlignment = NSTextAlignmentCenter;
    }
    return _displayLabel;
}

@end
viewController 代码示例
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end
#import "ViewController.h"
#import "CustomCollectionViewCell.h"
#import "CustomCollectionReusableView.h"

enum NSInteger {
    InsertItemTag = 100,
    DeleteItemTag
};

static NSString *const kTitle = @"CollectionView";
static NSString *const kCollectionViewCellIdentifier = @"collectionViewCellIdentifier";
static NSString *const kCollectionElementKindSectionHeaderIdentifier = @"headerIdentifier";

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout> {

    BOOL _selected;
    NSMutableArray *_dataSource; /**< 数据源 */
    NSMutableArray *_selectedDataArr; /**< 选中数据源 */
}

@property (nonatomic, strong) UICollectionView *collectionView; /**< 集合视图 */
@property (nonatomic, strong) UICollectionViewFlowLayout *collectionViewFlowLayout; /**< 流式布局 */


- (void)initializeDataSource; /**< 初始化数据源 */
- (void)initializeUserInterface; /**< 初始化用户界面 */

@end

@implementation ViewController

- (void)viewDidLoad {


    [super viewDidLoad];
    [self initializeDataSource];
    [self initializeUserInterface];
}

#pragma mark *** Initialize ***
- (void)initializeDataSource {

    _selectedDataArr = [NSMutableArray array];

    // 添加数据
    _dataSource = [NSMutableArray array];
    for (int i = 0; i < 20; i++) {
        [_dataSource addObject:[NSString stringWithFormat:@"%d", i]];
    }
}

- (void)initializeUserInterface {

    self.title = kTitle;
    self.automaticallyAdjustsScrollViewInsets = NO;


    // 视图加载
    [self.view addSubview:self.collectionView];

    // 配置导航栏
    // 插入
    UIBarButtonItem *insertItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
                                                                                target:self
                                                                                action:@selector(respondsToBarButtonItem:)];
    insertItem.tag = InsertItemTag;
    self.navigationItem.rightBarButtonItem = insertItem;


    // 删除
    UIBarButtonItem *deleteItem = [[UIBarButtonItem alloc] initWithTitle:@"删除"
                                                                   style:UIBarButtonItemStylePlain
                                                                  target:self
                                                                  action:@selector(respondsToBarButtonItem:)];
    deleteItem.tag = DeleteItemTag;
    self.navigationItem.leftBarButtonItem = deleteItem;


    // 添加长按手势,重排item
    UILongPressGestureRecognizer *longGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self
                                                                                              action:@selector(respondsToGesture:)];
    [self.collectionView addGestureRecognizer:longGesture];
}

#pragma mark *** Gestures ***
- (void)respondsToGesture:(UIGestureRecognizer *)gesture {

    NSLog(@"%@", NSStringFromSelector(_cmd));
    // 监听手势状态
    switch (gesture.state) {
            // 1、手势开始
        case UIGestureRecognizerStateBegan: {
            // 获取手势长按位置
            NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:[gesture locationInView:self.collectionView]];
            // 开始在特定的索引路径上对cell(单元)进行Interactive Movement(交互式移动工作)
            [self.collectionView beginInteractiveMovementForItemAtIndexPath:indexPath];

        }
            break;
            // 2、手势变换
        case UIGestureRecognizerStateChanged: {
            // 在手势作用期间更新交互移动的目标位置。
            [self.collectionView updateInteractiveMovementTargetPosition:[gesture locationInView:self.collectionView]];
        }
            break;
            // 3、手势结束
        case UIGestureRecognizerStateEnded: {
            // 在完成手势动作后,结束交互式移动
            [self.collectionView endInteractiveMovement];
        }
            break;
        default: {
            // 4、默认状态下,取消Interactive Movement。
            [self.collectionView cancelInteractiveMovement];
        }
            break;
    }
}

#pragma mark *** Events ***
- (void)respondsToBarButtonItem:(UIBarButtonItem *)sender {
    switch (sender.tag) {
        case InsertItemTag: {
            // 弹出框
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"请输入您要添加的数字:" preferredStyle:UIAlertControllerStyleAlert];
            [alertController.view layoutIfNeeded];
            // 添加文本输入框
            [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
                textField.clearButtonMode = UITextFieldViewModeWhileEditing;
                textField.textAlignment = NSTextAlignmentCenter;
            }];
            // 添加按钮
            [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:nil]];
            [alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
                // 获取文本输入框
                UITextField *textField = alertController.textFields[0];
                // 获取文本
                NSString *context = textField.text;
                // 异常处理
                if (context.length == 0) {
                    NSLog(@"请输入文本!");
                    return ;
                }
                // 数据源插入
                [_dataSource insertObject:context atIndex:0];
                // 单元格插入
                [_collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
            }]];

            [self presentViewController:alertController animated:YES completion:nil];
        }
            break;
        case DeleteItemTag: {

            if ([self.navigationItem.leftBarButtonItem.title isEqualToString:@"点击删除"]) {

                NSLog(@"执行删除逻辑!");

                // 遍历删除数据源数据
                for (NSString *data in _selectedDataArr) {
                    [_dataSource removeObject:data];
                }

                // 刷新集合视图
                [self.collectionView reloadData];

                self.collectionView.allowsSelection = NO;
            }

            // 切换编辑状态
            _selected = !_selected;

            self.navigationItem.leftBarButtonItem.title = _selected ? @"选择删除项" : @"删除";


           if ([self.navigationItem.leftBarButtonItem.title isEqualToString:@"选择删除项"]) {
               self.collectionView.allowsSelection = YES;
            }

        }
            break;

        default:
            break;
    }
}



#pragma mark *** UICollectionViewDataSource ***
// 设置组数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

// 设置行数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return _dataSource.count;
}

// 设置单元格
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    // item重用机制
    CustomCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCollectionViewCellIdentifier forIndexPath:indexPath];

    cell.displayLabel.backgroundColor = [UIColor whiteColor];
    cell.displayLabel.text = _dataSource[indexPath.row];

    return cell;
}

// 设置自定义头部、尾部视图
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {

    // UICollectionElementKindSectionHeader:头部视图
    // UICollectionElementKindSectionFooter:尾部视图
    // 如果同时自定义头部、尾部视图,可根据 kind 参数判断当前加载的是头部还是尾部视图,然后进行相应配置。

    // 重用机制
    CustomCollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kCollectionElementKindSectionHeaderIdentifier forIndexPath:indexPath];
    headerView.displayLabel.text = @"[自定义头部视图]";

    return headerView;
}

// 设置是否允许移动
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}

// 移动元素
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
    NSLog(@"%@", NSStringFromSelector(_cmd));

}

#pragma mark *** UICollectionViewDelegateFlowLayout ***
// 通过UICollectionViewDelegateFlowLayout提供的方法可高度定制流式布局;

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 10, 0, 10);
}

#pragma mark *** UICollectionViewDelegate ***
// 选中单元格
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    self.navigationItem.leftBarButtonItem.title = @"点击删除";
     _selected = YES;

    CustomCollectionViewCell *cell = (CustomCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
    cell.displayLabel.backgroundColor = [UIColor greenColor];

    [_selectedDataArr addObject:cell.displayLabel.text];
}

// 取消选中
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
    CustomCollectionViewCell *cell = (CustomCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
    cell.displayLabel.backgroundColor = [UIColor whiteColor];
    [_selectedDataArr removeObject:cell.displayLabel.text];
}
#pragma mark *** Getters ***
- (UICollectionViewFlowLayout *)collectionViewFlowLayout {
    if (!_collectionViewFlowLayout) {
        _collectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
        // 设置item估计值
        _collectionViewFlowLayout.estimatedItemSize = CGSizeMake(100, 100);
        // 全局配置item尺寸,单独定义调用协议方法[sizeForItemAtIndexPath]
        _collectionViewFlowLayout.itemSize = CGSizeMake(100, 100);
        // 全局配置每行之间的间距,单独定义可调用协议方法[minimumLineSpacingForSectionAtIndex]
        _collectionViewFlowLayout.minimumLineSpacing = 10;
        // 全局配置每行内部item的间距,单独定义可调用协议方法[minimumInteritemSpacingForSectionAtIndex]
        _collectionViewFlowLayout.minimumInteritemSpacing = 10;
        // 设置头部size
        _collectionViewFlowLayout.headerReferenceSize = CGSizeMake(CGRectGetWidth(self.view.bounds), 44);
        // 设置尾部size
        _collectionViewFlowLayout.footerReferenceSize = CGSizeZero;
        // 设置滚动方向
        // UICollectionViewScrollDirectionVertical
        // UICollectionViewScrollDirectionHorizontal
        _collectionViewFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
        // 设置是否当元素超出屏幕之后固定头部视图位置,默认NO;
        _collectionViewFlowLayout.sectionHeadersPinToVisibleBounds = YES;
        // 设置是否当元素超出屏幕之后固定尾部视图位置,默认NO;
        _collectionViewFlowLayout.sectionFootersPinToVisibleBounds = YES;


    }
    return _collectionViewFlowLayout;
}
- (UICollectionView *)collectionView {
    if (!_collectionView) {
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 64) collectionViewLayout:self.collectionViewFlowLayout];
        _collectionView.backgroundColor = [UIColor brownColor];
        // 设置是否允许滚动
        _collectionView.scrollEnabled = YES;
        // 设置是否允许选中,默认YES
        _collectionView.allowsSelection = NO;
        // 设置是否允许多选,默认NO
        _collectionView.allowsMultipleSelection = YES;
        // 设置代理
        _collectionView.delegate = self;
        // 设置数据源
        _collectionView.dataSource = self;


        // 注册Item
        [_collectionView registerClass:[CustomCollectionViewCell class] forCellWithReuseIdentifier:kCollectionViewCellIdentifier];

        // 注册头部视图
        [_collectionView registerClass:[CustomCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kCollectionElementKindSectionHeaderIdentifier];
    }
    return _collectionView;
}

@end
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值