美文网首页程序员
iOS 探探首页的卡片切换效果

iOS 探探首页的卡片切换效果

作者: RogueYBJ | 来源:发表于2018-09-02 20:41 被阅读0次

    效果图:

    tantan.gif

    最近公司要求写一个类似于探探的项目,在网上找了半天也没有找到合适的dome,所以就自己写了一个dome,希望能够帮到大家

    步骤:
    1.实现左右滑动
    2.实现删除或者还原
    3.实现左右旋转
    4.实现卡片的替换
    5.实现实现卡片的跟随

    首先自定义view实现1、2、3功能

    这里用到了UIView的类别的方法

    //UIView类别里的方法
    -(void)addPanAction:(SEL)action{
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:action];
        [self addGestureRecognizer:pan];
    }
    
    

    自定义UIView

    moreView.h

    @protocol moreViewdelegate <NSObject>
    
    //移动的距离
    -(void)moreVolue:(CGFloat)volue;
    //视图是否被移除
    -(void)moreisRemove:(BOOL)isRemove;
    
    @optional
    
    @end
    
    @interface moreView : UIView{
        CGPoint point;//保存点击初始位置
        CGRect rect;//保存视图初始位置
    }
    
    //喜欢
    @property (nonatomic, weak) UILabel * xh;
    //不喜欢
    @property (nonatomic, weak) UILabel * bxh;
    //协议
    @property(weak ,nonatomic)id<moreViewdelegate>delegates;
    
    

    两个协议方法是后面实现4、5需要用到的方法
    两个UIlabel是在视图上面的子控件(看需求添加)
    point成员变量是保存初始点击的位置(用于实现让图片跟随手指滑动)
    rect成员变量是保存视图的初始位置(用于还原到原来的位置)

    华丽的分割线==================================

    moreView.m

    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
    //        self.backgroundColor = [UIColor whiteColor];
            
            //布局UI
            [self LayoutUI];
            
            
            //添加拖动按钮
            self.userInteractionEnabled = NO;
    
            //这里是一个UIView的类别添加滑动的方法
            [self addPanAction:@selector(panAction:)];
        }
        return self;
    }
    
    -(void)panAction:(UIPanGestureRecognizer *)pan{
        UIView * view = (UIView *)pan.view;
        
        if (pan.state == UIGestureRecognizerStateBegan) {
            
            //存储准备开始滑动的坐标
            
            point = [pan locationInView: [view superview] ];
            
            rect = view.frame;
            
            
        }else if (pan.state == UIGestureRecognizerStateChanged){
            //监听滑动的距离并改变view的位置
            
            CGRect rects = rect;
            
            CGFloat x = [pan locationInView:[view superview]].x - point.x;
            
            CGFloat y = [pan locationInView:[view superview]].y - point.y;
            
            rects.origin = CGPointMake(rect.origin.x + x, rect.origin.y + y);
            
            view.frame = rects;
            
            //判断是否划出制定区域
            
            if (view.center.x > YBJ_ScreenW || view.center.x < 0 || view.center.y > YBJ_ScreenH || view.center.y < 0){
                if (view.center.x > YBJ_ScreenW) {
                    self.xh.alpha = 1;
                }
                if (view.center.x < 0) {
                    self.bxh.alpha = 1;
                }
                return;
            }
            
            //监听滑动的比例控制动画以及喜欢
            
            CGFloat xx = (view.center.x-(view.width/2))/(view.width/2);
            
            if (xx<0) {
                self.bxh.alpha = fabs(xx);
            }else{
                self.xh.alpha = fabs(xx);
            }
            
            [self viewAnimationfloat:(xx * 0.05)];
            
            [self.delegates moreVolue:xx];
            
            
        }else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateFailed){
            
            
            //判断是松手时是否划出制定区域
            if (view.center.x > YBJ_ScreenW || view.center.x < 0 || view.center.y > YBJ_ScreenH || view.center.y < 0) {
                [self removeFromSuperview];
                [self.delegates moreisRemove:YES];
            }else{
                
                [self.delegates moreisRemove:NO];
                [UIView animateWithDuration:0.2 animations:^{
                    self.xh.alpha = 0;
                    self.bxh.alpha = 0;
                    view.frame = self->rect;
                    [self viewAnimationfloat:0];
                }];
            }
        }
    }
    
    //CABasicAnimation动画
    -(void)viewAnimationfloat:(CGFloat)f{
        
        //根据z轴旋转
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        animation.toValue = [NSNumber numberWithFloat: M_PI * f];
        animation.duration = 0.2f;
        animation.autoreverses = NO;
        animation.removedOnCompletion = NO;
        animation.fillMode = kCAFillModeForwards;
        animation.repeatCount = 0;
        [self.layer addAnimation:animation forKey:nil];
        
    }
    

    1、LayoutUI这个方法是用于UI布局子控件用的。
    2、因为只能移动一张视图所以我们先将view的用户交互关掉。
    3、在panAction:方法中先获取到自身(用于对自身做相应的处理)。
    4、state刚点击下来我们就存储point 和 rect。
    5、state开始滑动时需要通过point实现图片跟随。
    6、设置指定范围用于控制喜欢和不喜欢控件(如果超出范围就return)。
    7、没有超出范围就通过滑动的比例控制喜欢喝不喜欢的透明度
    8、并且让view有左右旋转的功能。
    9、viewAnimationfloat:这个方法是通过比例决定旋转的角度。
    10、并且通过moreVolue:这个协议方法传递移动的比例(用于5的处理)。
    11、state松手时可通过制定的区域来决定还原还是删除。
    12、当滑动区域超过制定范围就删除该视图(这里可以添加一个往左移动的动画)。
    13、并且通过moreisRemove:这个协议方法传递是否划出制定范围(用于4的处理)。
    14、没有超过就还原到初始状态。
    15、并且通过moreisRemove:这个协议方法传递是否划出制定范围(用于4的处理)。

    华丽的分割线==============================

    调用moreView实现4、5功能

    Frame(x,y,width,height)这个宏是本人用于适配的(请自行改为自己的CGRectMake)

    moreView调用

    moreViewController.m

    #import "moreViewController.h"
    
    #import "moreView.h"
    
    #define baseFrame Frame(38,Height_NavBar + YBJ_ScreenW6(25),300,425)
    
    #define Frame1 Frame(0,0,300,400)
    
    #define Frame2 Frame(3,8,300-6,400)
    
    #define Frame3 Frame(6,16,300-12,400)
    
    #define Frame4 Frame(9,24,300-18,400)
    
    
    @interface moreViewController ()<moreViewdelegate>
    //背景视图
    @property (weak , nonatomic)UIView * baseView;
    //数据数组
    @property (strong ,nonatomic)NSMutableArray * dataArr;
    //more数组(视图)
    @property (strong ,nonatomic)NSMutableArray * moreArr;
    //位置数组(ps:因为位置不能存到数组需要转换,就没有用到)
    @property (strong ,nonatomic)NSMutableArray * FrameArr;
    
    @end
    
    @implementation moreViewController
    
    -(NSMutableArray *)dataArr{
        if (!_dataArr) {
            _dataArr = [NSMutableArray new];
            [_dataArr addObject:[UIColor redColor]];
            [_dataArr addObject:[UIColor orangeColor]];
            [_dataArr addObject:[UIColor grayColor]];
            [_dataArr addObject:[UIColor brownColor]];
            [_dataArr addObject:[UIColor greenColor]];
            [_dataArr addObject:[UIColor purpleColor]];
            [_dataArr addObject:[UIColor redColor]];
            [_dataArr addObject:[UIColor orangeColor]];
            [_dataArr addObject:[UIColor grayColor]];
            [_dataArr addObject:[UIColor brownColor]];
            [_dataArr addObject:[UIColor greenColor]];
            [_dataArr addObject:[UIColor purpleColor]];
        }
        return _dataArr;
    }
    
    -(NSMutableArray *)moreArr{
        if (!_moreArr) {
            _moreArr = [NSMutableArray new];
        }
        return _moreArr;
    }
    
    -(NSMutableArray *)FrameArr{
        if (!_FrameArr) {
            _FrameArr = [NSMutableArray new];
            
        }
        return _FrameArr;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //创建父视图
        
        [self setBaseView];
    //    [self addChildView];
    }
    
    /**
     创建父视图
     */
    
    -(void)setBaseView{
        
        UIView * baseView = [[UIView alloc]initWithFrame:baseFrame];
        [self.view addSubview:baseView];
        baseView.backgroundColor = [UIColor whiteColor];
        _baseView = baseView;
        
        for (int i = 0; i < 5 ; i++) {
            
            [self addChildView:i];
        }
    }
    
    /**
     添加子试图
     */
    
    -(void)addChildView:(NSInteger)i{
        
        if (self.dataArr.count == 0) {
            return;
        }
        
        moreView * more = [[moreView alloc]initWithFrame:CGRectMake(0+YBJ_ScreenW6(3*i), 0+8*i, YBJ_ScreenW6(300-(6*i)), YBJ_ScreenW6(400))];
        
        more.backgroundColor = self.dataArr[0];
        
        if (i == 0) {
            more.userInteractionEnabled = YES;
        }
        
        more.tag = i+10;
        
        switch (i) {
            case 0:
                more.frame = Frame1;
                break;
            case 1:
                more.frame = Frame2;
                break;
            case 2:
                more.frame = Frame3;
                break;
            case 3:
                more.frame = Frame4;
                break;
                
            default:
                more.frame = Frame4;
                break;
        }
        
        
        more.delegates = self;
        
        [self.baseView addSubview:more];
        
        [self.baseView sendSubviewToBack:more];
        
        [self.moreArr addObject:more];
        
        [self.dataArr removeObjectAtIndex:0];
        
    }
    /**
     moredelegates代理
     */
    -(void)moreVolue:(CGFloat)volue{
        
        NSLog(@"%f",volue);
        
        for (UIView * tepView in self.moreArr) {
            
            moreView * more = (moreView *)tepView;
            
            CGRect rect = CGRectZero;
            
            switch (more.tag-10) {
                case 0:
                    
                    break;
                case 1:
                    rect = Frame2;
                    rect.origin.x -= YBJ_ScreenW6(3*fabs(volue));
                    rect.origin.y -= YBJ_ScreenW6(8*fabs(volue));
                    rect.size.width += YBJ_ScreenW6(6*fabs(volue));
                    more.frame = rect;
                    break;
                case 2:
                    rect = Frame3;
                    rect.origin.x -= YBJ_ScreenW6(3*fabs(volue));
                    rect.origin.y -= YBJ_ScreenW6(8*fabs(volue));
                    rect.size.width += YBJ_ScreenW6(6*fabs(volue));
                    more.frame = rect;
                    break;
                case 3:
                    rect = Frame4;
                    rect.origin.x -= YBJ_ScreenW6(3*fabs(volue));
                    rect.origin.y -= YBJ_ScreenW6(8*fabs(volue));
                    rect.size.width += YBJ_ScreenW6(6*fabs(volue));
                    more.frame = rect;
                    break;
                    
                default:
                    
                    break;
            }
            
            
            
        }
        
    }
    
    -(void)moreisRemove:(BOOL)isRemove{
        if (isRemove) {
            
            [self.moreArr removeObjectAtIndex:0];
            
            
            if (self.moreArr.count == 0) {
                NSLog(@"没有了!");
            }
            
            for (UIView * tepView in self.moreArr) {
                
                moreView * more = (moreView *)tepView;
            
                more.tag -= 1;
                
                if (more.tag-10 == 0) {
                    more.userInteractionEnabled = YES;
                }
                
            }
            [self addChildView:4];
            
        }else{
            for (UIView * tepView in self.moreArr) {
                
                moreView * more = (moreView *)tepView;
                
                switch (more.tag - 10) {
                    case 0:
                        
                        break;
                    case 1:
                        more.frame = Frame2;
                        break;
                    case 2:
                        more.frame = Frame3;
                        break;
                    case 3:
                        more.frame = Frame4;
                        break;
                        
                    default:
                        break;
                }
            }
        }
    }
    

    1、上面一些宏定义分别为:背景、第一个、第二个、第三个、第四个~视图的位置(请自行更改)。
    2、上面一些属性分别为:背景视图、数据数组(这里添加了几条颜色数据)、more数组(视图)、位置数组(ps:因为位置不能存到数组需要转换,就没有用到)。
    3、setBaseView是创建父视图(用于做moreView的容器,设置属性是划完后能够remove,并且重新加载视图)。
    4、利用for循环来创建moreView(这里用了5个,因为有四个view的位置,所以留最后留一张垫底)。
    5、addChildView:这个方法是利用传递的i来设定tag值,并且设置位置,用moreArr存储moreView,sendSubviewToBack:这个方法将新添加的moreView放到最下面,然后删除dataArr中的第一个数据(当dataArr为空的时候就return)。
    6、moreViewdelegate签订协议(分别为:moreisRemove: 和 moreVolue: )。
    7、moreVolue: 通过这个协议方法实现5的效果,便利moreArr取出moreView,改变其frame实现跟随效果。
    8、moreisRemove: 通过这个协议4的效果。
    isRemove为真:
    moreArr删除第一个元素,便利moreArr取出moreView,改变其tag,让tag值-1,并且把第一个view改为可交互状态,再用addChildView:这个方法在底部添加一个moreView(当moreArr为空时就可以删除背景视图执行下一环节了)
    isRemove为假:
    便利moreArr取出moreView通过tag将其还原。

    end

    ps:因为moreView在滑动的后有还原操作,所以不需要还原。

    喜欢的小伙伴们可以点个💗加个关注!!!

    有问题的小伙伴私信我!GitHub/YBJTantan

    相关文章

      网友评论

        本文标题:iOS 探探首页的卡片切换效果

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