326 lines
11 KiB
Mathematica
326 lines
11 KiB
Mathematica
|
|
//
|
||
|
|
// MTGridSelectView.m
|
||
|
|
// MTGridSelectDemo
|
||
|
|
//
|
||
|
|
// Created by mambaxie on 2019/1/19.
|
||
|
|
// Copyright © 2019年 tencent. All rights reserved.
|
||
|
|
//
|
||
|
|
|
||
|
|
#import "MTGridSelectView.h"
|
||
|
|
#import "MTGridSelectConfig.h"
|
||
|
|
|
||
|
|
#define kItemContentViewCellReuseIdentifier @"kItemContentViewCellReuseIdentifier"
|
||
|
|
|
||
|
|
@interface MTGridSelectModel : NSObject
|
||
|
|
|
||
|
|
@property (nonatomic, strong) id model;
|
||
|
|
|
||
|
|
@property (nonatomic, assign) MTGridItemState state;
|
||
|
|
|
||
|
|
@property (nonatomic, strong) UIView *itemView;
|
||
|
|
|
||
|
|
@end
|
||
|
|
|
||
|
|
@implementation MTGridSelectModel
|
||
|
|
|
||
|
|
@end
|
||
|
|
|
||
|
|
@interface MTGridSelectView () <UICollectionViewDelegate, UICollectionViewDataSource>
|
||
|
|
|
||
|
|
@property (nonatomic, strong) MTGridSelectConfig *config;
|
||
|
|
|
||
|
|
@property (nonatomic, copy) void (^setupConfigBlock)(MTGridSelectConfig * _Nonnull);
|
||
|
|
@property (nonatomic, copy) MTGridDidSetupItemContentViewBlock didSetupItemContentViewBlock;
|
||
|
|
@property (nonatomic, copy) MTGridItemStateChangedBlock itemStateChangedBlock;
|
||
|
|
@property (nonatomic, copy) void (^selectValueChangedBlock)(NSArray *selectedModels);
|
||
|
|
|
||
|
|
@property (nonatomic, copy) NSArray<MTGridSelectModel *> *itemModels;
|
||
|
|
|
||
|
|
@property (nonatomic, strong) MTGridSelectModel *previousSelectItemModel;
|
||
|
|
@property (nonatomic, strong) NSMutableIndexSet *seletedIndexSet;
|
||
|
|
|
||
|
|
@end
|
||
|
|
|
||
|
|
@implementation MTGridSelectView
|
||
|
|
|
||
|
|
- (instancetype)init
|
||
|
|
{
|
||
|
|
if (self = [super init]) {
|
||
|
|
[self setupConfig];
|
||
|
|
}
|
||
|
|
return self;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (instancetype)initWithFrame:(CGRect)frame
|
||
|
|
{
|
||
|
|
if (self = [super initWithFrame:frame]) {
|
||
|
|
[self setupConfig];
|
||
|
|
}
|
||
|
|
return self;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (void)setupConfig
|
||
|
|
{
|
||
|
|
MTGridSelectConfig *config = [[MTGridSelectConfig alloc] init];
|
||
|
|
config.contentWidth = [UIScreen mainScreen].bounds.size.width;
|
||
|
|
config.contentInset = UIEdgeInsetsMake(15, 15, 15, 15);
|
||
|
|
config.rowSpacing = 10.0;
|
||
|
|
config.columnSpacing = 10.0;
|
||
|
|
config.maxColumn = 3;
|
||
|
|
config.itemRatio = 0.3;
|
||
|
|
config.itemHeight = 0.0;
|
||
|
|
config.itemSize = CGSizeZero;
|
||
|
|
config.multiple = NO;
|
||
|
|
config.maxSelecteCount = 3;
|
||
|
|
|
||
|
|
self.config = config;
|
||
|
|
}
|
||
|
|
|
||
|
|
+ (instancetype)gridSelectViewWithItemModels:(NSArray *)itemModels
|
||
|
|
setupConfig:(void(^)(MTGridSelectConfig *config))setupConfigBlock
|
||
|
|
didSetupItemContentView:(MTGridDidSetupItemContentViewBlock)didSetupItemContentViewBlock
|
||
|
|
itemStateChanged:(MTGridItemStateChangedBlock)itemStateChangedBlock
|
||
|
|
selectValueChanged:(void(^)(NSArray *selectedModels))selectValueChangedBlock
|
||
|
|
{
|
||
|
|
MTGridSelectView *grideSeletView = [[self alloc] init];
|
||
|
|
|
||
|
|
grideSeletView.seletedIndexSet = [[NSMutableIndexSet alloc] init];
|
||
|
|
grideSeletView.originalModels = itemModels;
|
||
|
|
NSMutableArray *itemModelsM = [NSMutableArray array];
|
||
|
|
for (id model in itemModels) {
|
||
|
|
MTGridSelectModel *itemModel = [[MTGridSelectModel alloc] init];
|
||
|
|
itemModel.state = MTGridItemStateNormal;
|
||
|
|
itemModel.model = model;
|
||
|
|
[itemModelsM addObject:itemModel];
|
||
|
|
}
|
||
|
|
grideSeletView.itemModels = [itemModelsM copy];
|
||
|
|
grideSeletView.setupConfigBlock = setupConfigBlock;
|
||
|
|
grideSeletView.didSetupItemContentViewBlock = didSetupItemContentViewBlock;
|
||
|
|
grideSeletView.itemStateChangedBlock = itemStateChangedBlock;
|
||
|
|
grideSeletView.selectValueChangedBlock = selectValueChangedBlock;
|
||
|
|
grideSeletView.clipsToBounds = NO;
|
||
|
|
[grideSeletView createAndLayoutItemContentViews];
|
||
|
|
|
||
|
|
return grideSeletView;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (void)setFrame:(CGRect)frame
|
||
|
|
{
|
||
|
|
[super setFrame:frame];
|
||
|
|
|
||
|
|
_collectionView.frame = self.bounds;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (NSArray<id> *)selectedItems
|
||
|
|
{
|
||
|
|
return [self.originalModels objectsAtIndexes:self.seletedIndexSet];
|
||
|
|
}
|
||
|
|
|
||
|
|
- (NSInteger)itemTotalCount
|
||
|
|
{
|
||
|
|
return self.originalModels.count;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (void)reloadDataWithItemModels:(NSArray *)itemModels
|
||
|
|
{
|
||
|
|
NSMutableArray *itemModelsM = [NSMutableArray array];
|
||
|
|
for (id model in itemModels) {
|
||
|
|
MTGridSelectModel *itemModel = [[MTGridSelectModel alloc] init];
|
||
|
|
itemModel.state = MTGridItemStateNormal;
|
||
|
|
itemModel.model = model;
|
||
|
|
[itemModelsM addObject:itemModel];
|
||
|
|
}
|
||
|
|
self.originalModels = itemModels;
|
||
|
|
self.itemModels = [itemModelsM copy];
|
||
|
|
[self.collectionView reloadData];
|
||
|
|
}
|
||
|
|
|
||
|
|
/// collectionViewCell 点击改变状态
|
||
|
|
- (void)changeItemViewStateAtIndex:(NSInteger)index
|
||
|
|
{
|
||
|
|
MTGridSelectModel *itemModel = self.itemModels[index];
|
||
|
|
MTGridItemState newState = MTGridItemStateNormal;
|
||
|
|
if (self.config.multiple) { // 多选
|
||
|
|
if (itemModel.state == MTGridItemStateNormal) {
|
||
|
|
// 超过上限
|
||
|
|
if (self.selectedItems.count >= self.config.maxSelecteCount) {
|
||
|
|
if (self.limitBlock) {
|
||
|
|
self.limitBlock();
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
newState = MTGridItemStateSelected;
|
||
|
|
} else if (itemModel.state == MTGridItemStateSelected) {
|
||
|
|
newState = MTGridItemStateNormal;
|
||
|
|
}
|
||
|
|
} else { // 单选
|
||
|
|
self.previousSelectItemModel.state = MTGridItemStateNormal;
|
||
|
|
NSInteger previousIndex = [self.itemModels indexOfObject:self.previousSelectItemModel];
|
||
|
|
[self.seletedIndexSet removeIndex:previousIndex];
|
||
|
|
if (self.previousSelectItemModel) {
|
||
|
|
self.itemStateChangedBlock(self.previousSelectItemModel.itemView, self.previousSelectItemModel.model, self.previousSelectItemModel.state, YES);
|
||
|
|
}
|
||
|
|
newState = MTGridItemStateSelected;
|
||
|
|
self.previousSelectItemModel = itemModel;
|
||
|
|
}
|
||
|
|
|
||
|
|
[self changeItemStateAtIndexs:@[@(index)] state:newState changeByInner:YES];
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 选中指定下标
|
||
|
|
- (void)selectItemAtIndex:(NSInteger)index
|
||
|
|
{
|
||
|
|
[self selectItemAtIndexs:@[@(index)]];
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 选中多个指定下标
|
||
|
|
- (void)selectItemAtIndexs:(NSArray <NSNumber *>*)indexs
|
||
|
|
{
|
||
|
|
[self changeItemStateAtIndexs:indexs state:MTGridItemStateSelected changeByInner:NO];
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 全选
|
||
|
|
- (void)selectAllItem
|
||
|
|
{
|
||
|
|
[self changeAllItemViewState:MTGridItemStateSelected];
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 全不选
|
||
|
|
- (void)deselectAllItem
|
||
|
|
{
|
||
|
|
[self changeAllItemViewState:MTGridItemStateNormal];
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 改变指定下标状态
|
||
|
|
- (void)changeItemStateAtIndex:(NSInteger)index state:(MTGridItemState)state
|
||
|
|
{
|
||
|
|
[self changeItemStateAtIndexs:@[@(index)] state:state changeByInner:NO];
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 改变多个下标状态
|
||
|
|
- (void)changeItemStateAtIndexs:(NSArray <NSNumber *>*)indexs state:(MTGridItemState)state changeByInner:(BOOL)changeByInner
|
||
|
|
{
|
||
|
|
BOOL isChanged = NO;
|
||
|
|
for (NSNumber *indexNumber in indexs) {
|
||
|
|
MTGridSelectModel *itemModel = self.itemModels[[indexNumber integerValue]];
|
||
|
|
if (itemModel.state != state) {
|
||
|
|
itemModel.state = state;
|
||
|
|
if (MTGridItemStateSelected == state) {
|
||
|
|
[self.seletedIndexSet addIndex:indexNumber.integerValue];
|
||
|
|
} else {
|
||
|
|
[self.seletedIndexSet removeIndex:indexNumber.integerValue];
|
||
|
|
}
|
||
|
|
if (self.itemStateChangedBlock) {
|
||
|
|
self.itemStateChangedBlock(itemModel.itemView, itemModel.model, itemModel.state, changeByInner);
|
||
|
|
}
|
||
|
|
isChanged = YES;
|
||
|
|
}
|
||
|
|
if (!self.config.multiple) { // 单选
|
||
|
|
self.previousSelectItemModel = itemModel;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (self.selectValueChangedBlock && isChanged) {
|
||
|
|
self.selectValueChangedBlock([self.originalModels objectsAtIndexes:self.seletedIndexSet]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
- (void)changeAllItemViewState:(MTGridItemState)state
|
||
|
|
{
|
||
|
|
NSMutableArray * array = [NSMutableArray array];
|
||
|
|
[self.itemModels enumerateObjectsUsingBlock:^(MTGridSelectModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||
|
|
[array addObject:@(idx)];
|
||
|
|
}];
|
||
|
|
[self changeItemStateAtIndexs:array state:state changeByInner:NO];
|
||
|
|
}
|
||
|
|
|
||
|
|
- (void)layoutSubviews
|
||
|
|
{
|
||
|
|
[super layoutSubviews];
|
||
|
|
|
||
|
|
CGRect frame = self.frame;
|
||
|
|
frame.size = self.collectionView.frame.size;
|
||
|
|
self.frame = frame;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (void)createAndLayoutItemContentViews
|
||
|
|
{
|
||
|
|
MTGridSelectConfig *config = self.config;
|
||
|
|
if (self.setupConfigBlock) {
|
||
|
|
self.setupConfigBlock(config);
|
||
|
|
}
|
||
|
|
|
||
|
|
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||
|
|
flowLayout.minimumLineSpacing = config.rowSpacing;
|
||
|
|
flowLayout.minimumInteritemSpacing = config.columnSpacing;
|
||
|
|
|
||
|
|
CGSize itemSize = config.itemSize;
|
||
|
|
|
||
|
|
if (CGSizeEqualToSize(itemSize, CGSizeZero)) {
|
||
|
|
itemSize.width = (config.contentWidth - config.contentInset.left - config.contentInset.right - config.columnSpacing * (config.maxColumn - 1)) / config.maxColumn;
|
||
|
|
itemSize.height = config.itemHeight > 0 ? config.itemHeight : itemSize.width * config.itemRatio;
|
||
|
|
}
|
||
|
|
flowLayout.itemSize = itemSize;
|
||
|
|
|
||
|
|
CGFloat contentHeight = config.contentInset.top + config.contentInset.bottom + (itemSize.height + config.rowSpacing) * (self.itemModels.count / config.maxColumn + 1) - config.rowSpacing;
|
||
|
|
CGFloat contentWidth = config.contentInset.left + config.contentInset.right + (itemSize.width + config.columnSpacing) * config.maxColumn - config.columnSpacing;
|
||
|
|
|
||
|
|
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, config.contentWidth, contentHeight) collectionViewLayout:flowLayout];
|
||
|
|
[self layoutSubviews];
|
||
|
|
self.collectionView.showsVerticalScrollIndicator = YES;
|
||
|
|
self.collectionView.showsHorizontalScrollIndicator = YES;
|
||
|
|
self.collectionView.contentSize = CGSizeMake(contentWidth, contentHeight);
|
||
|
|
self.collectionView.backgroundColor = [UIColor clearColor];
|
||
|
|
self.collectionView.contentInset = config.contentInset;
|
||
|
|
self.collectionView.dataSource = self;
|
||
|
|
self.collectionView.delegate = self;
|
||
|
|
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kItemContentViewCellReuseIdentifier];
|
||
|
|
[self addSubview:self.collectionView];
|
||
|
|
|
||
|
|
[self.collectionView reloadData];
|
||
|
|
|
||
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||
|
|
// 默认选中
|
||
|
|
[self selectItemAtIndexs:self.config.defaultSelectIndexs];
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
#pragma mark - UICollectionViewDelegate
|
||
|
|
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||
|
|
{
|
||
|
|
[collectionView deselectItemAtIndexPath:indexPath animated:NO];
|
||
|
|
|
||
|
|
[self changeItemViewStateAtIndex:indexPath.item];
|
||
|
|
}
|
||
|
|
|
||
|
|
#pragma mark - UICollectionViewDataSource
|
||
|
|
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||
|
|
{
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||
|
|
{
|
||
|
|
return self.itemModels.count;
|
||
|
|
}
|
||
|
|
|
||
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||
|
|
{
|
||
|
|
UICollectionViewCell *itemContentViewCell = [collectionView dequeueReusableCellWithReuseIdentifier:kItemContentViewCellReuseIdentifier forIndexPath:indexPath];
|
||
|
|
|
||
|
|
MTGridSelectModel *itemModel = self.itemModels[indexPath.item];
|
||
|
|
if (self.didSetupItemContentViewBlock) {
|
||
|
|
[itemContentViewCell.contentView removeSubviews];
|
||
|
|
UIView *itemView = self.didSetupItemContentViewBlock(itemContentViewCell.contentView, itemModel.model);
|
||
|
|
itemModel.itemView = itemView;
|
||
|
|
// 默认禁掉交互
|
||
|
|
itemView.userInteractionEnabled = NO;
|
||
|
|
[itemContentViewCell.contentView addSubview:itemView];
|
||
|
|
}
|
||
|
|
|
||
|
|
return itemContentViewCell;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
@end
|