iOS UICollectionView左右滚动和上下滑动处理

it2023-03-25  73

最近做了一个这样子的需求,在CollectionView上面移动Item并且不影响正常CollectionView的左右滑动处理。

当然TableView其实和CollectionView道理是一样的,可以采用同样的方式去处理

这里提供两种思路


第一种,在Item上面添加Pan手势处理,在Pan当中去做逻辑判断处理,然后手动改变contentOffset

Item里面的代码处理:

__block bool move = false; __block CGPoint beginPoint = CGPointZero; [self.scanImageView kj_AddGestureRecognizer:(KJGestureTypePan) block:^(UIView * _Nonnull view, UIGestureRecognizer * _Nonnull gesture) { CGPoint point = [gesture locationInView:view]; if (gesture.state == UIGestureRecognizerStateBegan) { beginPoint = [gesture locationInView:view]; }else if (point.y <= 5 && move == false) { move = true; CGPoint pt = [gesture locationInView:kKeyWindow]; if (weakself.kMoveBlock) weakself.kMoveBlock(weakself,pt,0); }else if (move && point.y < 5 && gesture.state == UIGestureRecognizerStateChanged) { CGPoint pt = [gesture locationInView:kKeyWindow]; if (weakself.kMoveBlock) weakself.kMoveBlock(weakself,pt,1); }else if (gesture.state == UIGestureRecognizerStateRecognized) { move = false; beginPoint = CGPointZero; CGPoint pt = [gesture locationInView:kKeyWindow]; if (weakself.kMoveBlock) weakself.kMoveBlock(weakself,pt,2); }else { point.x = beginPoint.x - point.x; point.y = beginPoint.y - point.y; if (weakself.kMoveTableBlock) weakself.kMoveTableBlock(weakself,point); } }];

CollectionView处的代码处理:

/// 拖动手势回调 cell.kMoveBlock = ^(UICollectionViewCell *item,CGPoint point,NSInteger move){ if (weakSelf.matterImageMoveBlock) { NSIndexPath *idx = kIndexpathSubviewCollectionview(item, self.collectView); weakSelf.matterImageMoveBlock(move,point,idx.row-1); } }; cell.kMoveTableBlock = ^(UICollectionViewCell *item,CGPoint point) { NSLog(@"---:%.2f,%.2f",weakSelf.collectView.contentOffset.x,point.x); if (abs(point.x) < 5) { return; } weakSelf.collectView.contentOffset = CGPointMake(point.x,0); };
总结:这种方式有个缺点就是手动改变contentOffset排除点不完美的情况,会导致左右拖动的时候出现CollectionView跳动的情况

第二种,写CollectionView的扩展获取Touch事件,方法交换处理完美解决

扩展代码H文件:

NS_ASSUME_NONNULL_BEGIN typedef NS_ENUM(NSInteger, KJMoveStateType) { KJMoveStateTypeBegin = 0, KJMoveStateTypeMove, KJMoveStateTypeEnd, KJMoveStateTypeCancelled, }; typedef void(^KJMoveBlock)(KJMoveStateType state,CGPoint point); @interface UICollectionView (KJTouch) /// 开启方法交换 @property(nonatomic,assign)bool kOpenExchange; /// Touch里面移动回调,需要开启方法交换才处理 @property(nonatomic,readwrite,copy)KJMoveBlock moveblock; @end NS_ASSUME_NONNULL_END

扩展代码M文件:

@implementation NSObject (Swizzling) + (BOOL)kj_swizzleMethod:(SEL)origSel Method:(SEL)altSel { Method origMethod = class_getInstanceMethod(self, origSel); Method altMethod = class_getInstanceMethod(self, altSel); if (!origMethod || !altMethod) return NO; class_addMethod(self, origSel, class_getMethodImplementation(self, origSel), method_getTypeEncoding(origMethod)); class_addMethod(self, altSel, class_getMethodImplementation(self, altSel), method_getTypeEncoding(altMethod)); method_exchangeImplementations(class_getInstanceMethod(self, origSel), class_getInstanceMethod(self, altSel)); return YES; } @end @implementation UICollectionView (Touch) - (bool)kOpenExchange{ return [objc_getAssociatedObject(self,@selector(kOpenExchange)) boolValue]; } - (void)setKOpenExchange:(bool)kOpenExchange{ objc_setAssociatedObject(self,@selector(kOpenExchange),[NSNumber numberWithBool:kOpenExchange],OBJC_ASSOCIATION_ASSIGN); } - (KJMoveBlock)moveblock{ return (KJMoveBlock)objc_getAssociatedObject(self, @selector(moveblock)); } - (void)setMoveblock:(KJMoveBlock)moveblock{ objc_setAssociatedObject(self, @selector(moveblock), moveblock, OBJC_ASSOCIATION_COPY_NONATOMIC); } + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self kj_swizzleMethod:@selector(touchesBegan:withEvent:) Method:@selector(kj_touchesBegan:withEvent:)]; [self kj_swizzleMethod:@selector(touchesMoved:withEvent:) Method:@selector(kj_touchesMoved:withEvent:)]; [self kj_swizzleMethod:@selector(touchesEnded:withEvent:) Method:@selector(kj_touchesEnded:withEvent:)]; [self kj_swizzleMethod:@selector(touchesCancelled:withEvent:) Method:@selector(kj_touchesCancelled:withEvent:)]; }); } - (void)kj_touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event{ if (self.kOpenExchange && self.moveblock) { CGPoint point = [[touches anyObject] locationInView:self]; self.moveblock(KJMoveStateTypeBegin,point); } [self kj_touchesBegan:touches withEvent:event]; } - (void)kj_touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event{ if (self.kOpenExchange && self.moveblock) { CGPoint point = [[touches anyObject] locationInView:self]; self.moveblock(KJMoveStateTypeMove,point); } [self kj_touchesMoved:touches withEvent:event]; } - (void)kj_touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event{ if (self.kOpenExchange && self.moveblock) { CGPoint point = [[touches anyObject] locationInView:self]; self.moveblock(KJMoveStateTypeEnd,point); } [self kj_touchesEnded:touches withEvent:event]; } - (void)kj_touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event{ if (self.kOpenExchange && self.moveblock) { CGPoint point = [[touches anyObject] locationInView:self]; self.moveblock(KJMoveStateTypeCancelled,point); } [self kj_touchesEnded:touches withEvent:event]; } @end

使用方式:

@interface KJCollectionVC ()<UICollectionViewDelegate,UICollectionViewDataSource> @property(nonatomic,strong)UICollectionView *collectView; @property(nonatomic,strong)UIImageView *imageView; @end @implementation KJCollectionVC - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self.view addSubview:self.collectView]; [self.view addSubview:self.imageView]; } - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView{ return 1; } - (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section{ return 10; } - (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"item" forIndexPath:indexPath]; cell.backgroundColor = [kRandomColor() colorWithAlphaComponent:0.9]; return cell; } #pragma mark - UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return CGSizeMake(60, 60); } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(10, 10, 10, 10); } - (UIImageView*)imageView{ if (!_imageView) { _imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 60, 60)]; _imageView.contentMode = UIViewContentModeScaleAspectFit; _imageView.hidden = YES; _imageView.layer.borderWidth = 2.; _imageView.layer.borderColor = UIColor.redColor.CGColor; _imageView.image = kGetImage(@"timg-2"); } return _imageView; } - (UICollectionView*)collectView{ if (_collectView == nil) { UICollectionViewFlowLayout *layOut = [[UICollectionViewFlowLayout alloc] init]; [layOut setScrollDirection:UICollectionViewScrollDirectionHorizontal]; layOut.minimumLineSpacing = 10; layOut.minimumInteritemSpacing = 10; _collectView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, kScreenH-70, kScreenW, 70) collectionViewLayout:layOut]; _collectView.delegate = self; _collectView.dataSource = self; _collectView.alwaysBounceHorizontal = YES; _collectView.showsHorizontalScrollIndicator = NO; _collectView.bounces = YES; _collectView.backgroundColor = [UIColor.greenColor colorWithAlphaComponent:0.3]; [_collectView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"item"]; _collectView.kOpenExchange = true; _weakself; __block bool move = false; _collectView.moveblock = ^(KJMoveStateType state, CGPoint point) { if (point.y <= 30 && move == false) { move = true; point = [weakself.collectView convertPoint:point toView:kKeyWindow]; NSIndexPath *idx = [weakself.collectView indexPathForItemAtPoint:point]; UICollectionViewCell *nextCell = [weakself.collectView cellForItemAtIndexPath:idx]; weakself.imageView.backgroundColor = nextCell.backgroundColor; weakself.imageView.center = point; weakself.imageView.hidden = NO; }else if (move && point.y < 30 && state == KJMoveStateTypeMove) { point = [weakself.collectView convertPoint:point toView:kKeyWindow]; weakself.imageView.center = point; weakself.collectView.scrollEnabled = NO; return; }else if (state == KJMoveStateTypeEnd || state == KJMoveStateTypeCancelled) { move = false; point = [weakself.collectView convertPoint:point toView:kKeyWindow]; weakself.imageView.center = CGPointZero; weakself.imageView.hidden = YES; } weakself.collectView.scrollEnabled = YES; }; } return _collectView; } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ @end
备注:本文用到的部分函数方法和Demo,均来自三方库**KJExtensionHandler**,如有需要的朋友可自行pod 'KJExtensionHandler'引入即可

CollectionView左右滚动和上下滑动处理介绍就到此完毕,后面有相关再补充,写文章不容易,还请点个**小星星**传送门

最新回复(0)