文件目录
效果图,绿色有序号的为选中的item
每个文件内容
viewController.m
#import "ViewController.h"
#import "MYCollectionViewCell.h"
#import "QYManager.h"
#import "MYModel.h"
#import "UIView+QYExtension.h"
static NSString *cellID = @"MYCollectionViewCell";
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource>
@property(nonatomic, strong) NSMutableArray *dataSource;
@property(nonatomic, strong) UICollectionView *myCollection;
@property (nonatomic, strong)UICollectionViewFlowLayout *layout;
// 手势
@property (assign, nonatomic) CGPoint panSelectStartPoint;
@property (assign, nonatomic) NSInteger currentPanSelectType;
@property (nonatomic, strong) QYManager *manager;
@property (strong, nonatomic) NSMutableArray *panSelectIndexPaths;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.myCollection];
[self createDataSource];
// 添加滑动手势
UIPanGestureRecognizer *selectPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(selectPanGestureRecognizerClick:)];
[self.view addGestureRecognizer:selectPanGesture];
}
- (void)createDataSource{
NSMutableArray *muArray = [NSMutableArray array];
NSArray *array = @[@"a", @"b", @"c", @"d", @"e", @"f", @"g", @"h", @"i", @"j", @"k", @"l", @"m", @"n", @"o", @"p", @"q", @"r", @"s", @"t", @"u", @"v", @"w", @"x", @"y", @"z"];
for (int i = 0; i < array.count; i++) {
MYModel *model = [[MYModel alloc] init];
model.name = array[i];
[muArray addObject:model];
}
self.dataSource = muArray;
[self.myCollection reloadData];
}
- (NSMutableArray *)dataSource
{
if(!_dataSource){
_dataSource = [NSMutableArray array];
}
return _dataSource;
}
// 手势操作
- (void)selectPanGestureRecognizerClick:(UIPanGestureRecognizer *)panGesture {
if (panGesture.state == UIGestureRecognizerStateBegan) {
// 获取起始点
self.panSelectStartPoint = [panGesture locationInView:self.myCollection];
NSIndexPath *indexPath = [self.myCollection indexPathForItemAtPoint:self.panSelectStartPoint];
if (indexPath) {
// 起始点在cell上
MYModel *firstModel = self.dataSource[indexPath.item];
self.currentPanSelectType = !firstModel.selected;
}else {
// 起始点不在cell上
self.currentPanSelectType = -1;
}
}else if (panGesture.state == UIGestureRecognizerStateChanged) {
CGPoint currentPoint = [panGesture locationInView:self.myCollection];
NSInteger firstLine = 0;
NSInteger lastLine = 0;
NSIndexPath *firstIndexPath = [self.myCollection indexPathForItemAtPoint:self.panSelectStartPoint];
if (!firstIndexPath) {
// 起始点不在cell上直接不可滑动选择
return;
}
NSIndexPath *lastIndexPath = [self.myCollection indexPathForItemAtPoint:currentPoint];
if (!lastIndexPath) {
return;
}
NSInteger rowCount = 3;
if ((firstIndexPath.item + 1) % rowCount == 0) {
firstLine = (firstIndexPath.item + 1) / rowCount;
}else {
firstLine = (firstIndexPath.item + 1) / rowCount + 1;
}
if ((lastIndexPath.item + 1) % rowCount == 0) {
lastLine = (lastIndexPath.item + 1) / rowCount;
}else {
lastLine = (lastIndexPath.item + 1) / rowCount + 1;
}
NSMutableArray *indexPaths = [NSMutableArray array];
CGFloat startX;
CGFloat maxX;
BOOL xReverse = NO;
if (currentPoint.x > self.panSelectStartPoint.x) {
// 向右
maxX = [self panSelectGetMaxXWithPoint:currentPoint];
startX = [self panSelectGetMinXWithPoint:self.panSelectStartPoint];
}else {
// 向左
xReverse = YES;
maxX = [self panSelectGetMaxXWithPoint:self.panSelectStartPoint];
startX = [self panSelectGetMinXWithPoint:currentPoint];
}
CGFloat maxY;
CGFloat startY;
BOOL yReverse = NO;
if (currentPoint.y > self.panSelectStartPoint.y) {
// 向下
maxY = [self panSelectGetMaxYWithPoint:currentPoint];
startY = [self panSelectGetMinYWithPoint:self.panSelectStartPoint];
}else {
// 向上
yReverse = YES;
maxY = [self panSelectGetMaxYWithPoint:self.panSelectStartPoint];
startY = [self panSelectGetMinYWithPoint:currentPoint];
}
NSInteger distanceW = self.layout.minimumInteritemSpacing + self.layout.itemSize.width;
NSInteger distanceH = self.layout.minimumInteritemSpacing + self.layout.itemSize.height;
BOOL canSelectVideo = YES;
NSIndexPath *sIndexPath = [self.myCollection indexPathForItemAtPoint:self.panSelectStartPoint];
if (sIndexPath && ![indexPaths containsObject:sIndexPath]) {
[indexPaths addObject:sIndexPath];
}
while (yReverse ? maxY > startY : startY < maxY) {
CGFloat tempStartX = startX;
CGFloat tempMaxX = maxX;
NSInteger currentLine = 0;
NSIndexPath *currentIndexPath;
if (yReverse) {
currentIndexPath = [self.myCollection indexPathForItemAtPoint:CGPointMake(tempMaxX - 1, maxY - 1)];
}else {
currentIndexPath = [self.myCollection indexPathForItemAtPoint:CGPointMake(tempStartX + 1, startY + 1)];
}
if ((currentIndexPath.item + 1) % rowCount == 0) {
currentLine = (currentIndexPath.item + 1) / rowCount;
}else {
currentLine = (currentIndexPath.item + 1) / rowCount + 1;
}
if (currentLine == firstLine) {
if (lastLine != firstLine) {
if (yReverse) {
tempMaxX = [self panSelectGetMaxXWithPoint:self.panSelectStartPoint];
tempStartX = 2;
}else {
if (xReverse) {
tempStartX = [self panSelectGetMinXWithPoint:self.panSelectStartPoint];
}
tempMaxX = SCREEN_WIDTH - 2;
}
}
}else if (currentLine == lastLine) {
if (yReverse) {
tempStartX = [self panSelectGetMinXWithPoint:currentPoint];
tempMaxX = SCREEN_WIDTH - 2;
}else {
tempStartX = 2;
if (xReverse) {
tempMaxX = [self panSelectGetMaxXWithPoint:currentPoint];
}
}
}else if (currentLine != firstLine && currentLine != lastLine) {
tempStartX = 2;
tempMaxX = SCREEN_WIDTH - 2;
}
while (yReverse ? tempMaxX > tempStartX : tempStartX < tempMaxX) {
NSIndexPath *indexPath;
if (yReverse) {
indexPath = [self panSelectCurrentIndexPathWithPoint:CGPointMake(tempMaxX, maxY) indexPaths:indexPaths canSelectVideo:canSelectVideo];
}else {
indexPath = [self panSelectCurrentIndexPathWithPoint:CGPointMake(tempStartX, startY) indexPaths:indexPaths canSelectVideo:canSelectVideo];
}
if (indexPath) {
[indexPaths addObject:indexPath];
}
if (yReverse) {
tempMaxX -= distanceW / 2;
}else {
tempStartX += distanceW / 2;
}
}
if (yReverse) {
maxY -= distanceH / 2;
}else {
startY += distanceH / 2;
}
}
NSIndexPath *eIndexPath = [self.myCollection indexPathForItemAtPoint:currentPoint];
if (eIndexPath && ![indexPaths containsObject:eIndexPath]) {
MYModel *model = self.dataSource[eIndexPath.item];
if (self.currentPanSelectType == 0) {
if (model.selected) {
[indexPaths addObject:eIndexPath];
}
}
else if (self.currentPanSelectType == 1) {
if (!model.selected) {
[indexPaths addObject:eIndexPath];
}
}
}
if (self.currentPanSelectType == -1) {
NSIndexPath *firstIndexPath = indexPaths.firstObject;
MYModel *firstModel = [[MYModel alloc] init];
if (firstIndexPath) {
firstModel = self.dataSource[firstIndexPath.item];
self.currentPanSelectType = !firstModel.selected;
}
}
for (NSIndexPath *indexPath in indexPaths) {
MYModel *model = self.dataSource[indexPath.item];
if (self.currentPanSelectType == 0) {
// 取消选择
if (model.selected) {
[self.manager beforeSelectedListdeletePhotoModel:model];
}
}
else if (self.currentPanSelectType == 1) {
// 选择
if (!model.selected) {
[self.manager beforeSelectedListAddPhotoModel:model];
}
}
}
[self.myCollection reloadData];
}else if (panGesture.state == UIGestureRecognizerStateEnded ||
panGesture.state == UIGestureRecognizerStateCancelled) {
self.panSelectIndexPaths = nil;
self.currentPanSelectType = -1;
}
}
- (CGFloat)panSelectGetMaxXWithPoint:(CGPoint)point {
NSIndexPath *indexPath = [self.myCollection indexPathForItemAtPoint:point];
UICollectionViewCell *cell = [self.myCollection cellForItemAtIndexPath:indexPath];
return CGRectGetMaxX(cell.frame) - 2;
}
- (CGFloat)panSelectGetMinXWithPoint:(CGPoint)point {
NSIndexPath *indexPath = [self.myCollection indexPathForItemAtPoint:point];
UICollectionViewCell *cell = [self.myCollection cellForItemAtIndexPath:indexPath];
return cell.qy_x + 2;
}
- (CGFloat)panSelectGetMaxYWithPoint:(CGPoint)point {
NSIndexPath *indexPath = [self.myCollection indexPathForItemAtPoint:point];
UICollectionViewCell *cell = [self.myCollection cellForItemAtIndexPath:indexPath];
return CGRectGetMaxY(cell.frame) - 2;
}
- (CGFloat)panSelectGetMinYWithPoint:(CGPoint)point {
NSIndexPath *indexPath = [self.myCollection indexPathForItemAtPoint:point];
UICollectionViewCell *cell = [self.myCollection cellForItemAtIndexPath:indexPath];
return cell.qy_y + 2;
}
- (NSIndexPath *)panSelectCurrentIndexPathWithPoint:(CGPoint)point indexPaths:(NSMutableArray *)indexPaths canSelectVideo:(BOOL)canSelectVideo {
NSIndexPath *indexPath = [self.myCollection indexPathForItemAtPoint:point];
if (indexPath && ![indexPaths containsObject:indexPath]) {
return indexPath;
}
return nil;
}
- (QYManager *)manager {
if (!_manager) {
_manager = [[QYManager alloc] init];
}
return _manager;
}
- (NSMutableArray *)panSelectIndexPaths {
if (!_panSelectIndexPaths) {
_panSelectIndexPaths = [NSMutableArray array];
}
return _panSelectIndexPaths;
}
#pragma mark- Delegate
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.dataSource.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MYCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
cell.model = self.dataSource[indexPath.row];
return cell;
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(([UIScreen mainScreen].bounds.size.width-40)/3.0, 80);
}
- (UICollectionView *)myCollection
{
if(!_myCollection){
_layout = [[UICollectionViewFlowLayout alloc]init];
_layout.minimumLineSpacing = 10;
_layout.minimumInteritemSpacing = 5;
_layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
_layout.sectionHeadersPinToVisibleBounds = YES;
_myCollection = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, [UIScreen mainScreen].bounds.size.height) collectionViewLayout:_layout];
_myCollection.backgroundColor = [UIColor whiteColor];
[_myCollection registerClass:[MYCollectionViewCell class] forCellWithReuseIdentifier:cellID];
_myCollection.delegate = self;
_myCollection.dataSource = self;
_myCollection.showsVerticalScrollIndicator = NO;
}
return _myCollection;
}
@end
MYCollectionViewCell.h
#import <UIKit/UIKit.h>
#import "MYModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface MYCollectionViewCell : UICollectionViewCell
@property(nonatomic, strong) MYModel *model;
@property (strong, nonatomic) UIButton *selectBtn;
@end
NS_ASSUME_NONNULL_END
MYCollectionViewCell.m
#import "MYCollectionViewCell.h"
@interface MYCollectionViewCell()
@property(nonatomic, strong)UILabel *label;
@end
@implementation MYCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self createCellUI];
}
return self;
}
-(void)createCellUI {
self.contentView.backgroundColor = [UIColor grayColor];
self.contentView.layer.cornerRadius = 10.0;
self.contentView.layer.masksToBounds = YES;
[self.contentView addSubview:self.label];
[self.contentView addSubview:self.selectBtn];
}
- (void)setModel:(MYModel *)model
{
self.label.text = model.name;
[self.selectBtn setTitle:model.selectIndexStr forState:UIControlStateNormal];
}
- (UILabel *)label
{
if(!_label){
_label = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, 50, 22)];
_label.textColor = [UIColor blueColor];
_label.font = [UIFont systemFontOfSize:14];
}
return _label;
}
- (UIButton *)selectBtn {
if (!_selectBtn) {
_selectBtn = [UIButton buttonWithType:UIButtonTypeCustom];
_selectBtn.backgroundColor = [UIColor greenColor];
_selectBtn.frame = CGRectMake(50, 20, 30, 30);
[_selectBtn setTitleColor:UIColor.blueColor forState:UIControlStateNormal];
_selectBtn.titleLabel.font = [UIFont systemFontOfSize:18];
_selectBtn.layer.cornerRadius = 15;
_selectBtn.layer.masksToBounds = YES;
}
return _selectBtn;
}
@end
QYManager
#import <Foundation/Foundation.h>
#import "MYModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface QYManager : NSObject
@property (strong, nonatomic) NSMutableArray *selectedList;
//完成之前从已选数组中删除某个模型
- (void)beforeSelectedListdeletePhotoModel:(MYModel *)model;
//完成之前添加某个模型到已选数组中
- (void)beforeSelectedListAddPhotoModel:(MYModel *)model;
//完成之前选择的所有数组
- (NSArray *)selectedArray;
@end
NS_ASSUME_NONNULL_END
QYManager.m
#import "QYManager.h"
@implementation QYManager
- (void)beforeSelectedListdeletePhotoModel:(MYModel *)model {
model.selected = NO;
model.selectIndexStr = @"";
model.selectedIndex = 0;
[self.selectedList removeObject:model];
int i = 0;
for (MYModel *model in self.selectedList) {
model.selectIndexStr = [NSString stringWithFormat:@"%d",i + 1];
i++;
}
}
- (void)beforeSelectedListAddPhotoModel:(MYModel *)model {
[self.selectedList addObject:model];
model.selected = YES;
model.selectedIndex = [self.selectedList indexOfObject:model];
model.selectIndexStr = [NSString stringWithFormat:@"%ld",model.selectedIndex + 1];
}
- (NSArray *)selectedArray {
return self.selectedList;
}
- (NSMutableArray *)selectedList {
if (!_selectedList) {
_selectedList = [NSMutableArray array];
}
return _selectedList;
}
@end
UIView+QYExtension.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (QYExtension)
@property (assign, nonatomic) CGFloat qy_x;
@property (assign, nonatomic) CGFloat qy_y;
@property (assign, nonatomic) CGFloat qy_w;
@property (assign, nonatomic) CGFloat qy_h;
@property (assign, nonatomic) CGFloat qy_centerX;
@property (assign, nonatomic) CGFloat qy_centerY;
@property (assign, nonatomic) CGSize qy_size;
@property (assign, nonatomic) CGPoint qy_origin;
@end
NS_ASSUME_NONNULL_END
UIView+QYExtension.m
#import "UIView+QYExtension.h"
@implementation UIView (QYExtension)
- (void)setQy_x:(CGFloat)qy_x
{
CGRect frame = self.frame;
frame.origin.x = qy_x;
self.frame = frame;
}
- (CGFloat)qy_x
{
return self.frame.origin.x;
}
- (void)setQy_y:(CGFloat)qy_y
{
CGRect frame = self.frame;
frame.origin.y = qy_y;
self.frame = frame;
}
- (CGFloat)qy_y
{
return self.frame.origin.y;
}
- (void)setQy_w:(CGFloat)qy_w
{
CGRect frame = self.frame;
frame.size.width = qy_w;
self.frame = frame;
}
- (CGFloat)qy_w
{
return self.frame.size.width;
}
- (void)setQy_h:(CGFloat)qy_h
{
CGRect frame = self.frame;
frame.size.height = qy_h;
self.frame = frame;
}
- (CGFloat)qy_h
{
return self.frame.size.height;
}
- (CGFloat)qy_centerX
{
return self.center.x;
}
- (void)setQy_centerX:(CGFloat)qy_centerX {
CGPoint center = self.center;
center.x = qy_centerX;
self.center = center;
}
- (CGFloat)qy_centerY
{
return self.center.y;
}
- (void)setQy_centerY:(CGFloat)qy_centerY {
CGPoint center = self.center;
center.y = qy_centerY;
self.center = center;
}
- (void)setQy_size:(CGSize)qy_size
{
CGRect frame = self.frame;
frame.size = qy_size;
self.frame = frame;
}
- (CGSize)qy_size
{
return self.frame.size;
}
- (void)setQy_origin:(CGPoint)qy_origin
{
CGRect frame = self.frame;
frame.origin = qy_origin;
self.frame = frame;
}
- (CGPoint)qy_origin
{
return self.frame.origin;
}
@end
MYModel.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface MYModel : NSObject
/// 是否选中
@property (assign, nonatomic) BOOL selected;
/// 选择的下标
@property (assign, nonatomic) NSInteger selectedIndex;
/// 模型所对应的选中下标
@property (copy, nonatomic) NSString * _Nullable selectIndexStr;
/// cell是否显示过
@property (assign, nonatomic) BOOL dateCellIsVisible;
// 测试用
@property (nonatomic, copy) NSString *name;
@end
NS_ASSUME_NONNULL_END
MYModel.m
#import "MYModel.h"
@implementation MYModel
- (void)setSelectIndexStr:(NSString *)selectIndexStr {
_selectIndexStr = selectIndexStr;
self.selectedIndex = selectIndexStr.integerValue - 1;
}
@end
复制上面所有的文件即可实现多选功能