通过前面的动力学小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例子】挂起的方块相关推荐

  1. C语言-第3章-格式化输出\输入

    文章目录 3.1 输出函数printf 3.1.1 细化的转换说明 3.1.2 转义序列 3.1.3 printf常见问题 3.2 输入函数scanf 3.2.1 scanf函数的工作过程 3.2.2 ...

  2. 独家 | 一文带你上手卷积神经网络实战(附数据集学习资料)

    原文标题:Understanding deep Convolutional Neural Networks with a practical use-case in Tensorflow and Ke ...

  3. 4.4 为什么使用深层表示-深度学习-Stanford吴恩达教授

    ←上一篇 ↓↑ 下一篇→ 4.3 核对矩阵的维数 回到目录 4.5 搭建深层神经网络快 为什么使用深层表示 (Why Deep Representation?) 我们都知道深度神经网络能解决好多问题, ...

  4. uva437巴比伦塔

    巴比伦人有n种长方形方块,每种有无限个,第i种方块的三边边长是xi,yi,zi.对于每一个方块,你可以任意选择一面作为底,这样高就随着确定了.举个例子,同一种方块,可能其中一个是竖着放的,一个是侧着放 ...

  5. 深度学习笔记第一门课​第四周:深层神经网络

    本文是吴恩达老师的深度学习课程[1]笔记部分. 作者:黄海广[2] 主要编写人员:黄海广.林兴木(第四所有底稿,第五课第一二周,第三周前三节).祝彦森:(第三课所有底稿).贺志尧(第五课第三周底稿). ...

  6. html5 canvas 笔记五(合成与裁剪)

    组合 Compositing globalCompositeOperation syntax: globalCompositeOperation = type 注意:下面所有例子中,蓝色方块是先绘制的 ...

  7. Android Weekly Notes Issue #218

    Android Weekly Issue #218 August 14th, 2016 http://androidweekly.net/issues/issue-218 ARTICLES & ...

  8. 深度学习入门笔记(八):深层网络的原理

    欢迎关注WX公众号:[程序员管小亮] 专栏--深度学习入门笔记 声明 1)该文章整理自网上的大牛和机器学习专家无私奉献的资料,具体引用的资料请看参考文献. 2)本文仅供学术交流,非商用.所以每一部分具 ...

  9. 《预训练周刊》第45期: 冻结语言模型、提示迁移性、快速文档排序

    No.45 智源社区 预训练组 预 训 练 研究 观点 资源 活动 周刊订阅 告诉大家一个好消息,<预训练周刊>已经开启"订阅功能",以后我们会向您自动推送最新版的&l ...

最新文章

  1. linux c 获取网络接口信息 ioct l函数 ifreq ifconf 结构体 简介
  2. python用中文怎么说-震惊!!!python可以用中文写代码
  3. 数据结构-判断一棵树是否为二叉排序树
  4. Linux笔记-Linux中的TracerPid
  5. ClipDrawable
  6. bootstrapinput传参数_bootstrap-fileinput组件在上传时传递额外参数
  7. 李兴华java开发实战经典---Java数据库编程
  8. xmind可以画流程图吗_如何用xmind做流程图
  9. VBA实例6 CorelDraw 批量生成设备位号、连续编号
  10. 知乎提示浏览器版本过低的完美解决办法
  11. 串口信号定义和接线方法-5针串口-9针串口-全功能串口
  12. 机器人学回炉重造(1):正运动学、标准D-H法与改进D-H法的区别与应用(附ABB机械臂运动学建模matlab代码)
  13. 毛孔很大很难看该怎么处理
  14. 游戏服务器稳定ping值,网友玩游戏时Ping值超过了2亿!
  15. android img 解包打包工具,Android系统system.img解包和重新打包
  16. 程序员的饭碗和杯具 .
  17. Hibernate快速入门(2)
  18. Squeezenet官方源代码解析
  19. 迅雷的工作原理 [揭密迅雷]
  20. (附源码)ssm网上书店系统 毕业设计 061436

热门文章

  1. 限制TensorFlow只在CPU上运行的方法
  2. 【VS开发】【电子电路技术】RJ45以太网传输线研究
  3. python数据库学习--Mysql
  4. 【C++】【十二】排序实现及思路
  5. Linux的rc.local自启动服务
  6. 当前日期得到本周的开始和结束日期
  7. 控件的呈现方法(Rendering)的内核
  8. DataGrid列操作
  9. C语言extern关键字定义外部变量--Redis源码extern使用
  10. 图像二值化----otsu(最大类间方差法、大津算法)(二)