效果图:
最近公司要求写一个类似于探探的项目,在网上找了半天也没有找到合适的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在滑动的后有还原操作,所以不需要还原。