iOS中的动力学:Dynamics【1】
iOS7建议我们创建的界面具有物理特性,而不只是像素的集合,可以响应触摸、手势、屏幕方向改变等事件,让用户与界面之间有更深入的交互,而不是像iOS6那样在软件界面上模仿现实世界的纹理而已。或许你会认为创建感觉上真实的界面比创建视觉上真实的界面要难的多。然而通过UIKit Dynamics 和 Motion Effects,这一切将变得非常简单。
•UIKit Dynamics是UIKit中的一个动力学引擎,通过它可以给界面添加重力、铰链连接,碰撞,悬挂等效果,让人感觉界面就是现实中存在的物体的。
开始编码吧
现在我们就从一些简单的小例子开始学习UIKit dynamic。
创建一个新的项目,在ViewController.m的viewDidLoad:中添加以下代码:
UIView* square = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
square.backgroundColor = [UIColor grayColor];
[self.view addSubview:square];
上面的代码在用户界面上添加了一个方块。运行项目可以看到:
尝试倾斜、旋转、摇动你的手机,额,好似什么都没发生。当往界面上添加view,它会根据你设置的frame而保持在对应的位置上。直到你给界面添加动力学效果!
添加重力
在ViewController.m中,添加全局变量:
UIDynamicAnimator* _animator;
UIGravityBehavior* _gravity;
在viewDidLoad:最后进行初始化:
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
_gravity = [[UIGravityBehavior alloc] initWithItems:@[square]];
[_animator addBehavior:_gravity];
文章后面会解释代码,现在只管运行项目。你会看见方块开始向下慢慢加速运动,一直到掉出屏幕底部:
上面添加的代码,有两个动力学数据类型
•UIDynamicAnimator是UIKit physics engine(下文统一称作"动力学引擎"),记录着你添加到引擎中的行为(behavior),例如重力,并提供上下文(context)。当你创建animator实例的时候,需要传入一个view对象作为参照系。
•UIGravityBehavior创建重力行为模型,并且作用于一个或多个物体上。当你创建一个behavior实例,将它与一系列物体(一般是view)相关联,这样你就可以设置哪些物体会受到behavior所影响,在这里就是受重力影响。
除了UIGravityBehavior,还有UIAttachmentBehavior、UICollisionBehavior、UIPushBehavior、UISnapBehavior等(下文统称”行为”)。大多数行为都有很多配置属性,例如,重力行为允许你修改重力的角度和大小,试着修改这些属性,使得物体以不同的加速度往上、往两边或斜着下落。
在现实世界中,重力加速度约为9.8m/s2,根据牛顿第二定律,可以用以下公式计算出在重力影响下物体的位移量:
distance = 0.5 × g × time<span style="font-size: 7px;">2</span>
在UIKit Dynamics中,以上也公式成立,但是使用的单位不一样,用等量化的点(point,之后简写为p)来代替。即UI重力加速度定义为1000p/s2。当然我们并不需要深入去研究,只需要知道g越大,物体就会下落得越快。
设置边界
在上面的效果中,方块在掉出屏幕底部后,会继续做下落运动,只是我们再也看不到罢了。为了使其保持在屏幕之内,需要定义边界,在 ViewController.m中添加一个新的实例变量:
UICollisionBehavior* _collision;
在viewDidLoad:中添加下面代码:
_collision = [[UICollisionBehavior alloc]initWithItems:@[square]];
_collision.translatesReferenceBoundsIntoBoundary = YES;
[_animator addBehavior:_collision];
上面的代码创建了一个碰撞行为。并且将translatesReferenceBoundsIntoBoundary属性设置为YES,而不是明确地添加边界坐标。这样设置之后,将以参考系的边作为边界。运行,可以看到方块会与屏幕的边界发生碰撞,并经过反弹后才停下来。
处理碰撞
下一步你会添加一个固定的障碍物,方块下落时将会与其发生碰撞。在viewDidLoad中添加以下代码:
UIView* barrier = [[UIView alloc] initWithFrame:CGRectMake(0, 300, 130, 20)];
barrier.backgroundColor = [UIColor redColor];
[self.view addSubview:barrier];
运行项目,可以看到红色的障碍物在屏幕中间。不过,障碍物并不起作用,方块下落时直接穿过障碍物。
这并不是我们期望的效果,不过这提醒了我们:dynamics只会对绑定了行为的view起作用。看一下示意图:
在前面的代码中,UIDynamicAnimator绑定了用来确定坐标系统的参照系视图,然后添加了一个或多个行为到关联的物体上,大多数行为可以关联到不同的物体上,每一个物体也可以关联多个不同的行为。上面的示意图展现了现在现在app中的行为和它们的关联关系。所有的行为都没”识别出”障碍物。所以动力学引擎并没有识别它。
使物体响应碰撞
为了让方块与障碍物发生碰撞,找到初始化碰撞行为的代码,改成如下:
_collision = [[UICollisionBehavior alloc] initWithItems:@[square, barrier]];
碰撞物体需要知道可以与其发生作用的所有对象,因此,将障碍物添加到可以发生碰撞的物体列表中。运行,可以看到下面情况:
更新前面的示意图,可以看到,碰撞行为现在已同时关联了两个view:
然而,两个物体之间的交互仍然和期望有所出入,我们希望障碍物是固定不可以移动的,但当两个物体发生碰撞时,障碍物被撞飞了!
更奇怪的是,障碍物最后并没有像方块那样停止在屏幕底部,因为障碍物并没有与重力行为关联,这也解释了为什么在没与方块发生碰撞之前,障碍物没有发生移动。
看起来好像需要别的方式来解决这个问题。因为障碍物是不可动的,并不需要动力学引擎来识别它,不过这又怎样检测到碰撞呢?
看不见的边界和碰撞
将碰撞行为的初始化代码改回最初的样子:
_collision = [[UICollisionBehavior alloc] initWithItems:@[square]];
然后,添加边界:
// add a boundary that coincides with the top edge
CGPoint rightEdge = CGPointMake(barrier.frame.origin.x +barrier.frame.size.width, barrier.frame.origin.y);
[_collision addBoundaryWithIdentifier:@"barrier"fromPoint:barrier.frame.origintoPoint:rightEdge];
上面的代码添加了一个看不见的边界,与障碍物的上边界重合。障碍物对用户仍然是可见的,但对于动力学引擎则是不可见的。而新添加的边界则是对动力学引擎是可见的,对用户是不可见的。当方块下落时,看起来会与障碍物发生作用,但其实是撞击了不可动的边界线!
运行,可以看到期望的效果:
现在,UIKit Dynamics的作用变得更加清晰了:你可以用几行代码来实现动力学效果。接着将继续介绍动力学引擎交互时的一些细节。
碰撞的背后
每一个动力学行为都有一个action的block类型属性,来监测动画的执行。在viewDidLoad:中添加:
_collision.action = ^{NSLog(@"%@, %@", NSStringFromCGAffineTransform(square.transform), NSStringFromCGPoint(square.center));
};
上面的代码会打印下落方块的center和transform属性。运行项目,你会看到Xcode控制台的log信息。在前400毫秒,log信息是这样的:
可以看到动力引擎正在改变方块的中心,当方块与障碍物发生碰撞,方块开始发生旋转,log信息输入变为这样:
可以看到动力引擎同时改变了方块的transform和position。
如果在动画过程中,我们通过代码改变方块的frame和transform属性,物体属性会被我们重写,也就是说,动力学控制过程中,我们不应该通过transform来缩放物体等。
动力行为赋予的对象是term items而不是view。因此,拥有动力行为的对象需要遵守UIDynamicItem协议:
@protocol UIDynamicItem <NSObject>@property (nonatomic, readwrite) CGPoint center;
@property (nonatomic, readonly) CGRect bounds;
@property (nonatomic, readwrite) CGAffineTransform transform;@end
UIDynamicItem对象允许动力学引擎读写center和transform属性,允许读取bounds属性。这允许在物体边缘创建碰撞的边界,以及计算物体的质量。这个协议说明动力学引擎不仅仅适用于UIView对象,其实还有另一个UIKit类UICollectionViewLayoutAttributes遵循这个协议。这允许力学引擎应用于collection view。
碰撞通知
接下来,我们看下怎么获取物体碰撞时的通知事件。
打开ViewController.m,添加遵从UICollisionBehaviorDelegate协议:
@interface ViewController () <UICollisionBehaviorDelegate>@end
在viewDidLoad中,在碰撞行为实例化之后,设置view controller作为代理对象:
_collision.collisionDelegate = self;
然后,添加代理方法:
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p {NSLog(@"Boundary contact occurred - %@", identifier);
}
这个代理方法在碰撞发生时执行,并在控制台打印log信息。为了避免控制台因太多信息而杂乱不堪,移除前面添加的_collision.action打印信息。运行,可以看:
在log信息中,可以看到方块与identifier为barrier的边界发生碰撞。这是前面添加的不可见边界。identifier为null的是参考系的边界。或许在反弹时添加一点视觉效果会更加有趣,在打印log信息后面添加:
UIView* view = (UIView*)item;
view.backgroundColor = [UIColor yellowColor];
[UIView animateWithDuration:0.3 animations:^{view.backgroundColor = [UIColor grayColor];
}];
上面的代码改变碰撞时物体的颜色为黄色,并变回灰色:
目前为止,都是动力学引擎自动设置物体的物理属性(例如质量和弹性)。接下来我们看下怎么通过UIDynamicItemBehavior类来自己控制这些物理属性。
配置物体属性
在viewDidLoad的最后,添加下面代码:
UIDynamicItemBehavior* itemBehaviour = [[UIDynamicItemBehavior alloc] initWithItems:@[square]];
itemBehaviour.elasticity = 0.6;
[_animator addBehavior:itemBehaviour];
上面的代码创建一个item行为,与方块关联。然后将行为添加到animator。elasticity属性控制了物体的弹性。1.0代表完全弹性碰撞。也就是,碰撞过程中没有能量和速度损耗。这里设置elasticity的值为0.6,表示方块会在每次碰撞后速度都会减少。
运行项目:
在上面的代码中,只是改变了物体的弹性。而行为对象还有很多可以设置的属性:
elasticity(弹性系数) – 决定了碰撞的弹性程度,比如碰撞时物体的弹性。
friction(摩擦系数) – 决定了沿接触面滑动时的摩擦力大小。
density(密度) – 跟 size 结合使用,来计算物体的总质量。质量越大,物体加速或减速就越困难。
resistance(阻力) – 决定线性移动的阻力大小,这根摩擦系数不同,摩擦系数只作用于滑动运动。
angularResistance(转动阻力) – 决定转动运动的阻力大小。
allowsRotation(允许旋转) – 这个属性很有意思,它在真实的物理世界没有对应的模型。设置这个属性为 NO 物体就完全不会转动,无力受到多大的转动力。
动态添加行为
目前,我们的应用设置系统的所有行为,然后由力学引擎处理系统的物理行为,直至所有物体静止。现在,看下如何动态添加或删除行为。
打开 ViewController.m 并添加如下实例变量:
BOOL _firstContact;
添加下面的代码到碰撞代理方法collisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint: 的末尾:
if (!_firstContact)
{_firstContact = YES;UIView* square = [[UIView alloc] initWithFrame:CGRectMake(30, 0, 100, 100)];square.backgroundColor = [UIColor grayColor];[self.view addSubview:square];[_collision addItem:square];[_gravity addItem:square];UIAttachmentBehavior* attach = [[UIAttachmentBehavior alloc] initWithItem:viewattachedToItem:square];[_animator addBehavior:attach];
}
上面的代码检测到方块和障碍物的第一次接触时,创建第二个方块并添加到碰撞和重力行为中。此外,设置了一个吸附行为,实现两个物体之间加入虚拟的弹簧的效果。
运行应用,当原方块撞到障碍物时,会看到一个新的方块出现
虽然两个方块看起来被连接到一起,但是因为没有在屏幕上画线条或是弹簧,你并不会看到视觉上的联系。
参考文献:https://www.raywenderlich.com/50197/uikit-dynamics-tutorial
相关文章:

自动化运维工具Saltstack(一)
1、saltstack简介: 什么是saltstack? saltstack是基于python开发的一套C/S架构配置管理工具 使用SSL证书签方的方式进行认证管理 号称世界上最快的消息队列ZeroMQ使得SaltStack能快速在成千上万台机器上进行各种操作 采用RSA Key方式确认身份 传输采用AE…
【UIDynamic例子】挂起的方块
通过前面的动力学小Demo(本文默认你已经看过这篇Blog:传送门),我们对UIKit中的UIDynamic已经有了初步的认识。现在我们写个更加有趣的Demo:模拟一个用弹性绳子挂起的小方块,用户可以将它拖动到屏幕任意位置…

IIS7 配置PHP服务器
安装PHP Manager: 1)访问 http://phpmanager.codeplex.com/releases/view/69115 下载PHP Manager。其中,x86 为32位 Windows 系统使用,x64 为64位 Windows 系统使用,请根据使用的 Windows 系统情况下载 2)下…
在文本框中提示用户输入内容格式的方法
希望达到的效果: 方法一:鼠标点击文本框时文字消失 <input id"login_name" type"text" οnblur"javascript:check_login_name();" οnfοcus"if(this.value支持英文及数字组合) this.value;this.style.color#000&…

saltstack实现haproxy+keepalived负载均衡+高可用(二)
一键部署haproxykeepalived实现负载均衡高可用 实验环境: !!!! 特别注意: www.westos.org为test1的minion名字 test1: 172.25.1.11 nginx master minion test2: 172.25.…

iOS开发技巧(系列十八:扩展UIColor,支持十六进制颜色设置)
新建一个Category,命名为UIColorHex,表示UIColor支持十六进制Hex颜色设置。 UIColorHex.h文件, #import <UIKit/UIKit.h> #define RGBA_COLOR(R, G, B, A) [UIColor colorWithRed:((R) / 255.0f) green:((G) / 255.0f) blue:((B) / 255…
iOS顶部菜单栏
封装的一个顶部菜单栏,使用懒加载(选择后加载当前页及前后各一页),自适应标题长度。 下载: Github:https://github.com/dolacmeng/JXChannelSegment 用法: //init Segment segment [[JXSegme…

Mac环境下svn的使用
CHENYILONG BlogMac环境下svn的使用 Mac环境下svn的使用 在Windows环境中,我们一般使用TortoiseSVN来搭建svn环境。在Mac环境下,由于Mac自带了svn的服务器端和客户端功能,所以我们可以在不装任何第三方软件的前提下使用svn功能,不…

zabbix简介及基本安装(一)
zabbix简单介绍: 官网:可以进官网查看一下:https://www.zabbix.com/cn/ //英语能力有限的读者可以将由上角的语言调成汉语方便查看 功能:网络监控、服务器监控、云监控、服务监控等。 介绍:zabbix([…

顺序表应用6:有序顺序表查询
顺序表应用6:有序顺序表查询 Time Limit: 7MS Memory Limit: 700KBSubmit StatisticProblem Description 顺序表内按照由小到大的次序存放着n个互不相同的整数(1<n<20000),任意输入一个整数,判断该整数在顺序表中是否存在。…

LA 5717枚举+最小生成树回路性质
1 /*LA 57172 《训练指南》P3433 最小生成树的回路性质4 在生成的最小生成树上,新增一条边e(u,v)5 若原图上u到v的路径的最大边大于e,则删除此边,加上e,否则不变。6 7 若原图上u到v的路径的最大边的产生:BFS/DFS都可 &…

【Runtime】动态添加方法demo
今天写一个小demo来演示下runtime的消息转发和动态添加方法。 一般项目中都会有保存当前登录用户资料的需求,我们可以直接将登录成功后的用户信息分别保存到NSUserDefaults中: [def setObject:"JackXu" forKey:"UserName"];[def set…

Zabbix之主机的添加与删除(二)
接着上一篇内容继续讲: 环境等都是建立在上一篇内容的基础上的,见https://blog.csdn.net/weixin_41922887/article/details/83755271 redhat6 test1: 172.25.1.11 zabbix-agent redhat7 server: 172.25.1.1 …

昨天网上感觉好冷,睡在席子上都是感觉打哈欠
今天爸妈也是休息一天,中午听说是要到外婆家去,不过家里就不知道会不会有一个团圆聚餐了,还有伴月就是国庆解,那时就要吧这个推掉值班的事情做好下。 转载于:https://www.cnblogs.com/bkchengzheng/p/5874328.html
几行代码实现神奇移动的过渡动画
1.效果如图: 2.实现: 假设需求为如上图,点击ViewController01后,ViewController01上的两张图片,移动到ViewContoller02中,其实两个ViewController的View上分别放置了这两张图,JXMagicMove就是实…

php字符串处理函数相关操作
<?php//获取tech和98426这两个字符串$str "http://info.meadin.com/tech/98426_1.shtml";echo $newstr substr($str,7,strlen($str)); //info.meadin.com/tech/98426_1.shtml$arr explode(/,$newstr);$num $arr[1];//tech$user strstr($arr[2], _, true); /…

介绍Zabbix的两种监控模式(主动模式和被动模式)
Zabbix agent检测分为两种模式:主动模式和被动模式 被动模式,也是默认的Zabbix监控模式,被动模式是相对于proxy来说的。proxy主动发送数据就是主动模式,proxy等待server的请求再发送数据就是被动模式。主动模式有个好处就是可以有…

【Step By Step】将Dotnet Core部署到Docker下
一、使用.Net Core构建WebAPI并访问Docker中的Mysql数据库 这个的过程大概与我之前的文章《尝试.Net Core—使用.Net Core Entity FrameWork Core构建WebAPI(一)》一致。 但是在我们这里,由于docker中无法部署sql server,所以我采…

ipad无法与itunes同步,提示因为这台电脑不再被授权使用在此ipad上购买的项目解决方案...
1、iOS设备用数据线连接到电脑;2、打开电脑上的iTunes 11,按CtrlB键调出菜单栏,按CtrlS键调出边栏;在边栏的 设备 下面看到你的iOS设备;3、点击菜单栏中的商店,点击 对这台电脑授权,输入你的App…

iOS根据字节数截取字符串
最近项目有个需求,文章的作者最多显示7个中文字,英文字符算半个中文字,超过7个中文字,则显示:前7个中文字...,使用NSString的length方法,不管是一个中文还是英文字符,都是返回1。因此…

搭建Zabbix分布式监控
1、实现zabbix监控nginx 实验环境: server1 172.25.1.1 server redhat7 test1 172.25.1.11 agent redhat7 在“手动添加”主机的基础上进行扩展 开启服务: [rootserver ~]# systemctl…

Codeforces Round #372 (Div. 2), problem: (B) Complete the Word
水题,每次截取长度为26的字符串,然后直接进行修改就可以 然而本弱渣昨天wa看很久 include<bits/stdc.h> using namespace std; int n,c; int ans[30]; int main() { string s; cin>>s; int tt0; int ns.size(); if(n<26) { cout<<&…

百练 2973 Skew数 解题报告
思路: 计算出每一个skew数的不同位数表示的权值,然后用该位与权值相乘。用int数组来装权值,用char数组来装skew数。 代码: #include<stdio.h> #include<string.h> int main() {int i, k, sum;int base[32];char skew[…

【Python】在Mac系统中安装Pygame
我们通过Homebrew来安装Pygame,Homebrew是Mac OSX上的软件包管理工具,如果还没安装Homebrew,将以下命令粘贴至终端先安装Homebrew /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install…

zabbix部署onealert云警告平台
onealert告警功能 告警 All In One,支持微信、邮箱、短信、APP、电话告警支持接入 Zabbix、Nagios、阿里云、腾讯云、监控宝等等告警信息灵活的分配策略,可灵活的分配告警信息发送给相关人员微信、邮箱、app 等告警方式全部免费实验环境: 首…

StringBuilder、StringBuffer、String区别
相信大家对 String 和 StringBuffer 的区别也已经很了解了,但是估计还是会有很多同志对这两个类的工作原理有些不清楚的地方,今天重新把这个概念给大家复习一下,顺便牵出 J2SE5.0 里面带来的一个新的字符操作的类—— StringBuilder …

Class中isAssignableFrom() 方法
看Spring源码的时候看到这个方法: 1 protected WebApplicationContext createWebApplicationContext(ServletContext sc) { 2 Class<?> contextClass determineContextClass(sc); 3 if (!ConfigurableWebApplicationContext.class.isAs…
【iOS】iOS10.3新增API:应用内评分
1、需求 在iOS10.3以前,APP引导用户评分时需要跳转到AppStore中操作,并且AppStore在国内有时加载会较慢,即便有的用户想给APP好评,但是等了几秒钟评分页面还没加载出来从而放弃。在iOS10.3中,苹果新增了APP内评分的新…

dhcp动态主机配置协议
dhcp简介: 动态主机设置协议(Dynamic Host Configuration Protocol,DHCP)是一个局域网的网络协议,使用UDP协议工作,计算机网络应用层协议。 主要有两个用途:用于内部网或网络服务供应商自动分配…

JSONP--解决ajax跨域问题
取不到数据! 上周客户新买了服务器,原本在旧的服务器上放着客户的Web主页信息和一个后台程序(asp.net),在客户的主页中有一个动态显示最新消息的处理,这个处理就是通过ajax异步从那个后台程序中取得的。由于又购买了新的服务器&am…