【UIDynamic例子】挂起的方块
来源:互联网 发布:工信部域名备案查询 编辑:程序博客网 时间:2024/05/20 20:17
通过前面的动力学小Demo(本文默认你已经看过这篇Blog:传送门),我们对UIKit中的UIDynamic已经有了初步的认识。现在我们写个更加有趣的Demo:模拟一个用弹性绳子挂起的小方块,用户可以将它拖动到屏幕任意位置,松手后小方块将在重力和绳子弹力的作用下进行运动,而运动过程完全由UIDynamic控制!
开始吧
新建项目,在ViewController中添加全局变量:
@interface ViewController (){ UIDynamicAnimator* _animator; UIGravityBehavior* _gravity; UIAttachmentBehavior *_attach; UIView *_plateView;}
上面代码分别声明动力学引擎、重力行为模型、连接行为模型、小方块的变量。
然后在viewDidLoad中进行初始化:
_plateView = [[UIView alloc] initWithFrame:CGRectMake(80, 150, 60, 60)];_plateView.backgroundColor = [UIColor orangeColor];[self.view addSubview:_plateView];_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];_gravity = [[UIGravityBehavior alloc] initWithItems:@[_plateView]];[_animator addBehavior:_gravity]; <p style="margin-top: 0px; margin-bottom: 0px; line-height: normal; font-family: Helvetica; color: rgb(69, 69, 69);">_attach = [[UIAttachmentBehavior alloc] initWithItem:_plateView attachedToAnchor:CGPointMake(200, 100)];</p>_attach.length = 60.0;_attach.damping = 0.1;_attach.frequency = 0.6;[_animator addBehavior:_attach];
上面的代码做了下面的事情:
1.在屏幕上添加了一个橘色的小方块。
2.创建并初始化动力学引擎,设置self.view作为参考系
3.创建并初始化重力行为模型,并与小方块关联,添加到动力学引擎中。
4.创建并初始化连接行为模型(下文统称为绳子吧),初始化时除了与小方块关联,还设置了锚点为(200,100)。另外还设置绳子的相关属性(长度、弹性、频率),最后添加到动力学引擎中。
运行项目:
已经可以看到方块在受到重力、绳子拉力的作用下,运动啦!不过还看不到绳子,这个需要我们自己画啦。另外,拖动小方块,什么事情都没发生。。。
添加绳子
现在我们自己来绘制绳子吧。体育老师说过两点确定一根直线,因此我们需要找到两个关键点,也就是锚点(也就是绳子顶端,我们就称作A点吧),以及绳子与方块的连接点(我们姑且称为B点)。点A已经由我们确定了是(200,100),点B我们取方块上边缘的中间,坐标是....?
好像这个坐标并不是固定的,在方块运动过程中,B也随着时间变动着。因此我们需要监听方块的运动,得到每一时刻B点的新坐标,将AB连点进行连接画线。怎样得到B点的最新坐标呢?我们可以通过KVO,监听方块center属性改变的事件,既在viewDidLoad最后添加:
[_plateView addObserver:self forKeyPath:@"center" options:NSKeyValueObservingOptionNew context:nil];
并实现回调方法:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{ [self updateLine];}
添加上面的方法后,只要方块运动,都会回调上面的方法,我们在方法里调用updateLine方法更新绳子,updateLine方法利用CAShapeLayer和贝塞尔曲线绘制绳子。
先声明一个CAShapeLayer类型的全局变量:
CAShapeLayer *_lineLayer;
完成updateLine方法,进行绳子的绘制:
-(void)updateLine{ if (nil == _lineLayer) { _lineLayer = [CAShapeLayer layer]; _lineLayer.strokeColor = [UIColor purpleColor].CGColor; _lineLayer.fillColor = [UIColor clearColor].CGColor; _lineLayer.lineWidth = 1.5f; _lineLayer.lineJoin = kCALineJoinRound; _lineLayer.strokeEnd = 1.0f; [self.view.layer addSublayer:_lineLayer]; } CGPoint platePoint = [self.view convertPoint:CGPointMake(CGRectGetMidX(_plateView.bounds), 0) fromView:_plateView]; UIBezierPath *bezierPath = [UIBezierPath bezierPath]; [bezierPath moveToPoint:_attach.anchorPoint]; [bezierPath addLineToPoint:platePoint]; _lineLayer.path = bezierPath.CGPath;}
在上面的代码中,platePoint即是我们需要的B点,这里使用了系统函数covertPoint:fromView,得到方块上边缘的中点在self.view坐标系中的坐标。
运行demo:
现在已经达到了我们的需求了?不过好似和最开头的效果图存在一点点差异,或许细心的你已经看出来,方块在水平方向上一直保持不变!这又是为什么呢?
优化调整
现在我们解决上面水平方向不变的小问题,回顾一下,我们初始化连接行为时,使用的是以下初始化方法:
_attach = [[UIAttachmentBehavior alloc] initWithItem:_plateView attachedToAnchor:CGPointMake(200, 100)];
这个方法,其实是将“绳子”和小方块的中心点连接起来了!如下图,连接的是C点,并非我们画线的B点
因此我们需要把连接点改为C点。UIAttachmentBehavior给我们提供了另一个方法:
_attach = [[UIAttachmentBehavior alloc] initWithItem:_plateView offsetFromCenter:UIOffsetMake(0, -30) attachedToAnchor:CGPointMake(200, 100)];
这个方法比原来的多了一个offsetFromCenter的参数,类型为一个UIOffset结构体,结构体包含了水平和竖直方向上的偏移量,从D点到C点,只要向上移动30个单位,也就是在竖直方向上偏移-30即可。
现在再运行项目:
完美!
添加手势
给方块添加拖动手势:
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];[_plateView addGestureRecognizer:pan];
当用户拖动view时,会回调pan:方法,我们在此方法中,改变方块的位置:
- (void)pan:(UIGestureRecognizer *)pan{ pan.view.center = [pan locationInView:self.view];}
运行项目,尝试拖动方块......
这是什么鬼!或许你忘记了上一篇Blog中的"碰撞的背后"说过不应该修改view的transform属性,当然我们也不应该修改center属性。
那么我们就在开始拖动的时候,移除所有的行为,当拖动结束之后再把行为添加回去,将pan:函数修改为如下:
- (void)pan:(UIGestureRecognizer *)pan{ switch (pan.state) { case UIGestureRecognizerStateBegan: [_animator removeAllBehaviors]; break; case UIGestureRecognizerStateChanged: pan.view.center = [pan locationInView:self.view]; break; case UIGestureRecognizerStateEnded: [_animator addBehavior:_gravity]; [_animator addBehavior:_attach]; break; default: break; }}
再次运行项目,拖动滑块看看!
微调微调!
上一篇讲到,我们可以配置物体的属性,即弹性、密度、阻力等等,这个大家可以尝试各个属性不同的值,看看对方块运动产生什么影响。示例:
UIDynamicItemBehavior* itemBehaviour = [[UIDynamicItemBehavior alloc] initWithItems:@[_plateView]]; itemBehaviour.elasticity = 0.6; [_animator addBehavior:itemBehaviour];
封装
先下载啦:https://github.com/dolacmeng/JXDynamicsDemo
我做了简单封装,只要下面几行代码,即可将带绳子的方块添加到项目中:
JXDynamics *dy = [[JXDynamics alloc] initWithFrame:CGRectMake(110, 200, 50, 50)];[self.view addSubview:dy];[dy setUpWithAnchor:CGPointMake(100, 100) inView:self.view];
如果想响应点击事件:
dy.tapBlock = ^{ NSLog(@"tap!");};
JXDynamics是UIView子类,你可以在上面添加UI控件,例如UILabel:
UILabel *label = [[UILabel alloc] initWithFrame:dy.bounds];label.text = @"Hello";label.textAlignment = NSTextAlignmentCenter;[dy addSubview:label];
效果如图:
当然,你也可以自定义类继承自JXDynamics,详见demo。
原创文章,转载标注出处:http://blog.csdn.net/dolacmeng/article/details/52301621
- 【UIDynamic例子】挂起的方块
- UIDynamic的基本使用
- UIDynamic的使用
- UIdynamic的简单使用
- UIDynamic
- UIDynamic
- 简单的多线程创建,执行,挂起,终止的例子
- QThread一个最简单的挂起,恢复的例子
- pthread一个最简单的挂起,恢复的例子
- 用汉编做的方块
- 黑皮的小方块
- 浮躁的方块
- JS移动的方块
- 方块的旋转
- 练习:疯狂的方块
- 旋转的方块
- 运动的方块
- WINE中文字体方块的解决
- iOS中UIScrollViewDelegate使用总结
- Java 多线程实现死锁场景 (r)
- POJ 3159 松弛操作 差分约束 SPFA
- 使用SortedMap对HashMap排序
- 【小项目】简单天气预报项目的实现与流程
- 【UIDynamic例子】挂起的方块
- Java内部类的作用
- iOS持续集成,让你开发不在困难
- Java 多线程 join和interrupt 方法 (r)
- rails下正确使用国外yahoo的第三方登录插件
- CSRF 攻击的应对之道
- RGB、YUV和YCbCr(YUV 4:4:4...)
- Struts2中simple主题下的国际化处理
- nginx查看post请求日志