美文网首页iOS分享世界
一个实用的TableView滑动效果

一个实用的TableView滑动效果

作者: honzon_0 | 来源:发表于2020-10-24 14:38 被阅读0次

    首先看效果

    QQ20201024-140424-HD的副本.gif

    关键点

    自定义手势

    很明显的需要一个自定义拖动手势

    //拖拽手势
        UIPanGestureRecognizer *tableViewPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureAction:)];
        tableViewPan.delegate = self;
        self.animationPanGes = tableViewPan;
        [self addGestureRecognizer:tableViewPan];
    

    跟随拖拽手势移动视图

    这里移动视图并不一定要是tableView,虽然拖拽手势是添加在tableView上面,但是可以设置为tableView的父视图,具体自行实践

    @protocol MTVerticalAnimationTableViewDelegate <NSObject>
    /**跟随拖动手势移动的View*/
    - (UIView *)mt_verticalAnimationTableViewPanGestureMovingView:(MTVerticalAnimationTableView *)tableView;
    @end
    

    这里通过代理来控制移动的视图

    处理自定义拖动手势与tableView的拖动手势冲突

    1. 设置tableView的拖动手势在自定义拖动手势失效之后生效
    //tableView滚动手势依赖于自定义手势失败
    [self.panGestureRecognizer requireGestureRecognizerToFail:tableViewPan];
    
    1. 手势代理设置自定义手势失效条件
    //列表滚动  只响应tableView拖动手势
    if (self.contentOffset.y > 0) {
        return NO;
    }
    
    //contentOffset.y = 0时  1.向上拖动,tableView拖动手势响应  2.向下拖动,自定义拖动手势响应
    if (self.contentOffset.y == 0 && translatedPoint.y < 0) {
        return NO;
    }
    
    1. 设置自定义手势生效时的操作
    /**拖动手势中*/
    CGRect frame = movingView.frame;
    frame.origin.y += translation.y;
    //改变frame
    movingView.frame = frame
    
    1. 设置自定义手势结束时的操作
    /**拖动手势结束*/
    CGRect frame = movingView.frame;
    frame.origin.y += translation.y;
    
     //确定最终Y
        MTAnimationTableViewState state = MTAnimationTableViewStateBottom;
        if (frame.origin.y > self.centerStateOriginY && frame.origin.y <= self.bottomStateOriginY) {
            frame.origin.y = self.bottomStateOriginY;
        } else {
            if (self.state == MTAnimationTableViewStateTop && frame.origin.y > self.topStateOriginY) {
                frame.origin.y = self.centerStateOriginY;
                state = MTAnimationTableViewStateCenter;
            } else {
                frame.origin.y = self.topStateOriginY;
                state = MTAnimationTableViewStateTop;
            }
        }
    
    //改变frame
    movingView.frame = frame
    

    所有代码

    MTVerticalAnimationTableView.h文件

    #import <UIKit/UIKit.h>
    
    /*
     MTAnimationTableViewStateTop
     1.列表向上滑动 正常滑动
     2.列表向下滑动 a.没有滑动到顶部,正常滑动  b.滑动到顶部,整体向下
     
     MTAnimationTableViewStateCenter
        1.列表向上滑动 禁止滑动 整体向上
        2.列表向下滑动 禁止滑动 整体向下
     
     MTAnimationTableViewStateBottom
     */
    typedef enum : NSUInteger {
        MTAnimationTableViewStateTop,
        MTAnimationTableViewStateCenter,
        MTAnimationTableViewStateBottom,
    } MTAnimationTableViewState;
    
    @protocol MTVerticalAnimationTableViewDelegate;
    
    @interface MTVerticalAnimationTableView : UITableView
    @property (nonatomic, weak)id<MTVerticalAnimationTableViewDelegate>animationDelegate;
    /**位置枚举*/
    @property (nonatomic, assign, readonly)MTAnimationTableViewState state;
    
    /**Top PanGestureMovingView的frame.origin.y   默认 = (安全区域高度+导航栏高度)*/
    @property (nonatomic, assign)CGFloat topStateOriginY;
    /**Center PanGestureMovingView的frame.origin.y  默认 = (安全区域高度+导航栏高度-topStateOriginY)/2*/
    @property (nonatomic, assign)CGFloat centerStateOriginY;
    /**Bottom PanGestureMovingView的frame.origin.y  默认 = (屏幕高度)*/
    @property (nonatomic, assign)CGFloat bottomStateOriginY;
    
    /**创建*/
    - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style state:(MTAnimationTableViewState)state;
    
    /**显示*/
    - (void)showAnimationTableViewCompletion:(dispatch_block_t)completion;
    
    /**隐藏*/
    - (void)hiddenAnimationTableViewCompletion:(dispatch_block_t)completion;
    @end
    
    
    @protocol MTVerticalAnimationTableViewDelegate <NSObject>
    /**跟随拖动手势移动的View*/
    - (UIView *)mt_verticalAnimationTableViewPanGestureMovingView:(MTVerticalAnimationTableView *)tableView;
    
    @optional
    /**拖动手势移动的View动画结束*/
    - (void)mt_verticalAnimationTableViewPanGestureEnded:(MTVerticalAnimationTableView *)tableView;
    @end
    

    MTVerticalAnimationTableView.m文件

    #import "MTVerticalAnimationTableView.h"
    
    @interface MTVerticalAnimationTableView ()<UIGestureRecognizerDelegate>
    /**拖动手势*/
    @property (nonatomic, strong)UIPanGestureRecognizer *animationPanGes;
    
    /**位置枚举*/
    @property (nonatomic, assign, readwrite)MTAnimationTableViewState state;
    @end
    
    @implementation MTVerticalAnimationTableView
    - (void)dealloc{
        NSLog(@"MTAnimationTableView dealloc");
    }
    
    #pragma mark - Public
    - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style state:(MTAnimationTableViewState)state{
        self = [super initWithFrame:frame style:style];
        if (self) {
            self.state = state;
            //设置默认限制
            [self setupAnimationTableViewStateOriginY];
            //设置头部
            [self setupSubViews:style];
        }
        return self;
    }
    
    /**显示*/
    - (void)showAnimationTableViewCompletion:(dispatch_block_t)completion {
        /**已经弹出不做处理*/
        if (self.state != MTAnimationTableViewStateBottom) {
            if (completion) {
                completion();
            }
            return;
        }
        
        if ([self.animationDelegate respondsToSelector:@selector(mt_verticalAnimationTableViewPanGestureMovingView:)]) {
            CGRect frame = [self _panGestureMovingViewFrame];
            frame.origin.y = self.centerStateOriginY;
            [self _animationFrame:frame changeState:MTAnimationTableViewStateCenter completion:completion];
        }
    }
    
    /**隐藏*/
    - (void)hiddenAnimationTableViewCompletion:(dispatch_block_t)completion {
        CGRect frame = [self _panGestureMovingViewFrame];
        frame.origin.y = self.bottomStateOriginY;
        [self _animationFrame:frame changeState:MTAnimationTableViewStateBottom completion:completion];
    }
    
    #pragma mark - UI
    /**设置默认限制*/
    - (void)setupAnimationTableViewStateOriginY {
        CGFloat ret = 64;
        if (@available(iOS 11.0,*)) {
            CGFloat navBarHeight = 44.0f;
            ret = [UIApplication sharedApplication].statusBarFrame.size.height + navBarHeight;
    
        }
        self.topStateOriginY = ret;
        self.centerStateOriginY = ([UIScreen mainScreen].bounds.size.height+ret)/2;
        self.bottomStateOriginY = [UIScreen mainScreen].bounds.size.height;
    }
    
    - (void)setupSubViews:(UITableViewStyle)style {
        //拖拽手势
        UIPanGestureRecognizer *tableViewPan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureAction:)];
        tableViewPan.delegate = self;
        self.animationPanGes = tableViewPan;
        [self addGestureRecognizer:tableViewPan];
        
        //tableView滚动手势依赖于自定义手势失败
        [self.panGestureRecognizer requireGestureRecognizerToFail:tableViewPan];
    }
    
    #pragma mark - Action
    /**拖动手势*/
    - (void)panGestureAction:(UIPanGestureRecognizer *)panGes {
        CGPoint translation = [panGes translationInView:panGes.view];
        //拖动一直是在递增,所以每次都要用setTranslation:inView:方法置为0
        [panGes setTranslation:CGPointMake(0, 0) inView:panGes.view];
        
        if(panGes.state ==UIGestureRecognizerStateBegan) {
            
        } else if(panGes.state ==UIGestureRecognizerStateChanged) {
            [self panGestureRecognizerChanging:translation];
        } else if(panGes.state ==UIGestureRecognizerStateEnded) {
            [self panGestureRecognizerDidChange:translation];
        }
    }
    
    /**拖动手势中*/
    - (void)panGestureRecognizerChanging:(CGPoint)translation {
        CGRect frame = [self _panGestureMovingViewFrame];
        frame.origin.y += translation.y;
        
        //不小于最小Y
        if ([self _compareCGfloat:frame.origin.y CGFloat2:self.topStateOriginY] == NSOrderedAscending) {
            frame.origin.y = self.topStateOriginY;
        }
        //不大于最大Y
        if ([self _compareCGfloat:frame.origin.y CGFloat2:self.bottomStateOriginY] == NSOrderedDescending) {
            frame.origin.y = self.bottomStateOriginY;
        }
    
        //改变frame
        if ([self.animationDelegate respondsToSelector:@selector(mt_verticalAnimationTableViewPanGestureMovingView:)]) {
            [self.animationDelegate mt_verticalAnimationTableViewPanGestureMovingView:self].frame = frame;
        }
    }
    
    /**拖动手势结束*/
    - (void)panGestureRecognizerDidChange:(CGPoint)translation {
        CGRect frame = [self _panGestureMovingViewFrame];
        frame.origin.y += translation.y;
        //不小于最小Y
        if ([self _compareCGfloat:frame.origin.y CGFloat2:self.topStateOriginY] == NSOrderedAscending) {
            frame.origin.y = self.topStateOriginY;
        }
        //不大于最大Y
        if ([self _compareCGfloat:frame.origin.y CGFloat2:self.bottomStateOriginY] == NSOrderedDescending) {
            frame.origin.y = self.bottomStateOriginY;
        }
        
        //    //确定最终Y
        //    MTAnimationTableViewState state = MTAnimationTableViewStateBottom;
        //    if (frame.origin.y < self.centerStateOriginY) {
        //        frame.origin.y = self.topStateOriginY;
        //        state = MTAnimationTableViewStateTop;
        //    } else if(frame.origin.y >= self.centerStateOriginY && frame.origin.y < (self.bottomStateOriginY+self.centerStateOriginY)/2) {
        //        frame.origin.y = self.centerStateOriginY;
        //        state = MTAnimationTableViewStateCenter;
        //    } else {
        //        frame.origin.y = self.bottomStateOriginY;
        //    }
        
        //确定最终Y
        MTAnimationTableViewState state = MTAnimationTableViewStateBottom;
        if ([self _compareCGfloat:frame.origin.y CGFloat2:self.centerStateOriginY] == NSOrderedDescending && [self _compareCGfloat:frame.origin.y CGFloat2:self.bottomStateOriginY] != NSOrderedDescending) {
            frame.origin.y = self.bottomStateOriginY;
        } else {
            if (self.state == MTAnimationTableViewStateTop && [self _compareCGfloat:frame.origin.y CGFloat2:self.topStateOriginY] == NSOrderedDescending) {
                frame.origin.y = self.centerStateOriginY;
                state = MTAnimationTableViewStateCenter;
            } else {
                frame.origin.y = self.topStateOriginY;
                state = MTAnimationTableViewStateTop;
            }
        }
        
        //动画
        [self _animationFrame:frame changeState:state completion:^{
            //动画结束
            if ([self.animationDelegate respondsToSelector:@selector(mt_verticalAnimationTableViewPanGestureEnded:)]) {
                [self.animationDelegate mt_verticalAnimationTableViewPanGestureEnded:self];
            }
        }];
    }
    
    #pragma mark - Private
    - (void)_animationFrame:(CGRect)frame changeState:(MTAnimationTableViewState)changeState completion:(dispatch_block_t)completion{
        [UIView animateWithDuration:0.3 animations:^{
            //改变frame
            if ([self.animationDelegate respondsToSelector:@selector(mt_verticalAnimationTableViewPanGestureMovingView:)]) {
                [self.animationDelegate mt_verticalAnimationTableViewPanGestureMovingView:self].frame = frame;
            }
        } completion:^(BOOL finished) {
            if (finished) {
                self.state = changeState;
            
                if (completion) {
                    completion();
                }
            }
        }];
    }
    
    - (CGRect)_panGestureMovingViewFrame {
        if ([self.animationDelegate respondsToSelector:@selector(mt_verticalAnimationTableViewPanGestureMovingView:)]) {
            UIView *moveingView = [self.animationDelegate mt_verticalAnimationTableViewPanGestureMovingView:self];
            return moveingView.frame;
        }
        return CGRectZero;
    }
    
    /**CGFloat比较方法  解决精度丢失问题*/
    - (NSComparisonResult)_compareCGfloat:(CGFloat)float1 CGFloat2:(CGFloat)float2 {
        /**精度差值  决定是否相等*/
        float positiveDifference = 0.001;
        float negativeDifference = -0.001;
        CGFloat result = float1 - float2;
        if (result < negativeDifference) {
            return NSOrderedAscending;
        } else if(result > positiveDifference) {
            return NSOrderedDescending;
        } else {
            return NSOrderedSame;
        }
    }
    
    #pragma mark - UIGestureRecognizerDelegate
    /**处理自定义手势生效失效*/
    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
        if (gestureRecognizer == self.animationPanGes && self.state == MTAnimationTableViewStateTop) {
            CGPoint translatedPoint = [self.animationPanGes translationInView:self.animationPanGes.view];
            
            //列表滚动  只响应tableView拖动手势
            if (self.contentOffset.y > 0) {
                return NO;
            }
            
            //contentOffset.y = 0时  1.向上拖动,tableView拖动手势响应  2.向下拖动,自定义拖动手势响应
            if (self.contentOffset.y == 0 && translatedPoint.y < 0) {
                return NO;
            }
        }
    
        return YES;
    }
    
    - (void)setTopStateOriginY:(CGFloat)topStateOriginY {
        _topStateOriginY = ceil(topStateOriginY);
    }
    
    @end
    

    相关文章

      网友评论

        本文标题:一个实用的TableView滑动效果

        本文链接:https://www.haomeiwen.com/subject/qdwimktx.html