登录 立即注册
金钱:

Code4App-iOS开发-iOS 开源代码库-iOS代码实例搜索-iOS特效示例-iOS代码例子下载-Code4App.com

iOS 狂霸酷炫拽之Button动效

[复制链接]
来自: 李世铿 分类: iOS精品源码 上传时间: 2016-7-14 17:07:01
Tag:Button 动画 动效 按钮 Button

项目介绍:

自定义Button  ( github @ coderZsq/coderZsq.project.ios)



这里我们使用
CAShapeLayer + UIBezierPath将Button给画了出来, 其实完全可以让设计做图, 我这里只是因为抛砖引玉后面的内容!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[Objective-C] 查看源文件 复制代码
- (CAShapeLayer *)roundShapeLayer {
    
    if (!_roundShapeLayer) {
        _roundShapeLayer = [CAShapeLayer layer];
        _roundShapeLayer.backgroundColor = KC01_57c2de.CGColor;
        _roundShapeLayer.borderColor = KC05_dddddd.CGColor;
        _roundShapeLayer.borderWidth = 0.5f;
        _roundShapeLayer.masksToBounds = YES;
    }
    return _roundShapeLayer;
}

- (CAShapeLayer *)horizontalShapeLayer {
    
    if (!_horizontalShapeLayer) {
        _horizontalShapeLayer = [CAShapeLayer layer];
        _horizontalShapeLayer.fillColor = [UIColor whiteColor].CGColor;
    }
    return _horizontalShapeLayer;
}

- (CAShapeLayer *)verticalShapeLayer {
    
    if (!_verticalShapeLayer) {
        _verticalShapeLayer = [CAShapeLayer layer];
        _verticalShapeLayer.fillColor = _horizontalShapeLayer.fillColor;
    }
    return _verticalShapeLayer;
}

- (void)setupSubviews {
    [self setAlpha:0.7f];
    [self.layer addSublayer:self.roundShapeLayer];
    [self.layer addSublayer:self.horizontalShapeLayer];
    [self.layer addSublayer:self.verticalShapeLayer];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    CGFloat roundShapeLayerX = 0;
    CGFloat roundShapeLayerY = 0;
    CGFloat roundShapeLayerW = self.width;
    CGFloat roundShapeLayerH = roundShapeLayerW;
    self.roundShapeLayer.cornerRadius = roundShapeLayerW * 0.5f;
    self.roundShapeLayer.frame = CGRectMake(roundShapeLayerX, roundShapeLayerY, roundShapeLayerW, roundShapeLayerH);
    
    CGFloat horizontalPathW = self.width - 12;
    CGFloat horizontalPathH = self.height / 6;
    CGFloat horizontalPathX = (self.width - horizontalPathW) * 0.5f;
    CGFloat horizontalPathY = (self.height - horizontalPathH) * 0.5f;
    self.horizontalShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(horizontalPathX, horizontalPathY, horizontalPathW, horizontalPathH) cornerRadius:10].CGPath;
    
    CGFloat verticalPathW = horizontalPathH;
    CGFloat verticalPathH = horizontalPathW;
    CGFloat verticalPathX = (self.width - verticalPathW) * 0.5f;;
    CGFloat verticalPathY = (self.height - horizontalPathW) * 0.5f;;
    self.verticalShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(verticalPathX, verticalPathY, verticalPathW, verticalPathH) cornerRadius:10].CGPath;
}
2. 可移动Button

将上面创建好的Button 继承与SQExtension中的SQRemovableButton 即可实现移动属性!! 这里我来看下可移动属性的实现原理!!(其实完全可以使用Pan手势实现!!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[Objective-C] 查看源文件 复制代码
@interface SQRemovableButton ()

@property (nonatomic,assign,getter = isMoved) BOOL moved;

@end

@implementation SQRemovableButton

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    
    [super touchesMoved:touches withEvent:event];
    
    self.moved = YES;
    
    UITouch * touch = [touches anyObject];
    CGPoint current = [touch locationInView:self];
    CGPoint previous = [touch previousLocationInView:self];
    
    CGPoint center = self.center;

    center.x += current.x - previous.x; center.y += current.y - previous.y;
    
    CGFloat screenWidth  = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    
    CGFloat xMin = self.frame.size.width  * 0.5f; CGFloat xMax = screenWidth  - xMin;
    CGFloat yMin = self.frame.size.height * 0.5f; CGFloat yMax = screenHeight - yMin - 49;
    
    if (center.x > xMax) center.x = xMax; if (center.y > yMax) center.y = yMax;
    if (center.x < xMin) center.x = xMin; if (center.y < yMin) center.y = yMin;
    
    self.center = center;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    
    if (!self.moved) {
        [super touchesEnded:touches withEvent:event];
    }
        self.moved = NO; if (!self.dockable) return;
    
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat           x = self.frame.size.width * 0.5f;
    
    [UIView animateWithDuration:0.25f animations:^{
        CGPoint center = self.center;
        center.x = self.center.x > screenWidth * 0.5f ? screenWidth - x : x;
        self.center = center;
    }];
}

这里有个难点在于当滑动的时候会和Touch Events/Tap Gesture 发生冲突, 解决方法在于当其移动的时候不调用其父类的方法即可!!

3. Button的Pop动画

使用SQExtension中的CAAnimation+Extension即可实现pop动画, 这个我不多说, 关键帧动画的基本实现!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Objective-C] 查看源文件 复制代码
+ (CAAnimation *)animationPopWithLayer:(CALayer *)layer {
    
    CAKeyframeAnimation * pop = [CAKeyframeAnimation animation];
    pop.keyPath     = @"transform.scale";
    pop.values      = @[@0.1, @0.2, @0.3, @0.2, @0.1];
    pop.additive    = YES;
    
    CAAnimationGroup * group = [CAAnimationGroup new];
    group.animations = @[pop];
    group.duration = kTimeInterval;
    group.removedOnCompletion = NO;
    [layer addAnimation:group forKey:nil];
    return group;
}

我们同样将其加在navigationController.view 上并设定初始布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (SQLifestylePostButton *)postButton {
   
    CGFloat postButtonW = 40;
    CGFloat postButtonH = postButtonW;
    CGFloat postButtonX = self.view.width - kSpace - postButtonW;
    CGFloat postButtonY = self.view.height - 49 - kSpace - postButtonH;
   
    if (!_postButton) {
        _postButton = [SQLifestylePostButton new];
        _postButton.frame = CGRectMake(postButtonX, postButtonY, postButtonW, postButtonH);
    }
    [CAAnimation animationPopWithLayer:_postButton.layer];
    return _postButton;
}

并将点击事件在Button内部实现, 那我们怎么在view的内部拿到当前控制器呢?
之前我的老大在项目中是用单例设计模式实现的可以参考SQExtension中的SQViewControllerManager.h, 今天我分享一个更加投机的方式, 全局变量!!
我们先将所有的控制器都继承与SQViewController, 并实现如下方法;

.h

1
extern UIViewController * kCurrentViewController;

.m

1
UIViewController * kCurrentViewController = nil;

SQViewController.m

1
2
3
4
[Objective-C] 查看源文件 复制代码
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    kCurrentViewController = self;
}

这样我们就能够拿到当前的控制器了 whenTap方法我不多讲,底层是用runtime实现的, 就是为了让代码高内聚,低耦合,(关键是runtime真的要讲很久…)

1
2
3
4
5
[Objective-C] 查看源文件 复制代码
__weak typeof(self) _self = self;
[self whenTapped:^{
    SQNavigationController * navigationController = [[SQNavigationController alloc]initWithRootViewController:[SQPostViewController new]];
    [kCurrentViewController presentViewController:navigationController animated:YES completion:nil];
}];

到此为止, 我们的Button已经具有不错的动画效果了~~

4. Button的自定义转场动画

自定义转场, 我之前就有提及到我就快速的讲, 今天我们来在Modal的自定义转场, 自定义转场两步走~

1 设置代理

1
2
3
4
5
6
7
8
9
   
[Objective-C] 查看源文件 复制代码
@interface SQLifestylePostButton () UIViewControllerTransitioningDelegate>[/align]        
  [navigationController setTransitioningDelegate:_self];
  
  - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    SQHoleAnimatedTransitioning * animatedTransitioning = [SQHoleAnimatedTransitioning new];
    animatedTransitioning.frame = self.frame;
    return animatedTransitioning;
}

2 实现自定义转场

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[Objective-C] 查看源文件 复制代码
@interface SQHoleAnimatedTransitioning ()

@property (nonatomic, strong) id<UIViewControllerContextTransitioning> transitionContext;

@end

static const CGFloat kRatio = 1.5f;

@implementation SQHoleAnimatedTransitioning

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
    return 0.25f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    self.transitionContext = transitionContext;
    
    UIViewController * toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController * fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIView * containView = transitionContext.containerView;
    [containView addSubview:toViewController.view];
    [containView addSubview:fromViewController.view];
    
    UIView * endView = [UIView new];
    endView.frame = self.frame;
    UIBezierPath * endPath = [UIBezierPath bezierPathWithOvalInRect:endView.frame];
    
    UIView * startView = [UIView new];
    startView.center = endView.center;
    startView.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height * kRatio, [UIScreen mainScreen].bounds.size.height * kRatio);
    UIBezierPath * startpath = [UIBezierPath bezierPathWithOvalInRect:startView.frame];
    
    CAShapeLayer * maskLayer = [CAShapeLayer layer];
    
    maskLayer.path = endPath.CGPath;
    fromViewController.view.layer.mask = maskLayer;
    
    CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.fromValue = (__bridge id )(startpath.CGPath);
    animation.toValue = (__bridge id )(endPath.CGPath);
    animation.duration = [self transitionDuration:self.transitionContext];
    animation.delegate = self;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [maskLayer addAnimation:animation forKey:nil];
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    
    [self.transitionContext completeTransition:![self.transitionContext transitionWasCancelled]];
    [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
}

之前我有看过用象限算法实现的, 擦, 好复杂… 本宝宝最不喜欢的就是计算这种费脑子的事情了, 能够投机的, 就不要那么麻烦嘛, 这里我借用了两个view来实现相同功能, 我感觉这里需要细讲一下,不然有些同学不是很好理解…

self.frame 就是可移动Button最终的frame, 然后借助一个View来确定第二个Rect的Center, 其实就是同心圆, 有没有很好理解?? 然后使用mask属性进行路径动画就可以了, mask其实就Ps中的蒙版, 遮罩, 对mask属性不是很熟悉的同学可以Google一下alpha通道, 简单来说就是, mask不透明, 就能够穿透, 好像就是这个意思~


DEMO 直接下载:


相关源码推荐:

我来说两句
*滑动验证:
所有评论(23)
songismxs 2016-7-14 18:20:23
正需要啊,感谢楼主无私分享!
回复
王颖博 2016-7-15 10:50:38
感谢分享,Code4App有你更精彩
回复
a395027720 2016-7-15 15:50:58
淡定,淡定,淡定……
回复
code4app热心网友 2016-7-15 15:51:09
楼主用心了,内容非常精彩。
回复
ichinaapplewei 2016-7-16 09:48:50
楼主威武啊,Code4App有你更给力!
回复
a395027720 2016-7-16 20:54:07
感谢分享,Code4App有你更精彩:)
回复
code4app热心网友 2016-7-16 20:54:11
我只是路过打酱油的。
回复
dulong1548 2016-7-16 23:37:21
我只是路过打酱油的。
回复
nanMenHaiShao 2016-7-17 02:04:13
感谢分享,Code4App有你更精彩
回复
123下一页
提取码:  下载次数:47 状态:已购或VIP 售价:0(原价:10)金钱 下载权限:初级码农 
903 0 47
联系我们
首页/微信公众账号投稿

帖子代码编辑/版权问题

QQ:435399051,742864542

如何获得代码达人称号?

代码贡献英雄榜
用户名 下载数
通过邮件订阅最新 Code4App 信息
上一条 /4 下一条
联系我们
关闭
合作电话:
13802416937
Email:
435399051@qq.com
商务市场合作/投稿
问题反馈及帮助
联系我们

广告投放| Github|申请友链|手机版|Code4App ( 粤ICP备15117877号-1 )

快速回复 返回顶部 返回列表