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

效果图:

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 

//移动的距离
-(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)iddelegates;

两个协议方法是后面实现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 ()
//背景视图
@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 探探首页的卡片切换效果)