UIKit框架(一) —— UIKit动力学和移动效果(一)

版本记录

版本号 时间
V1.0 2018.08.05

前言

iOS中有关视图控件用户能看到的都在UIKit框架里面,用户交互也是通过UIKit进行的,这一篇就看一下UIKit动力学和移动效果。

简介

UIKit Dynamics是集成到UIKit的完整物理引擎。 它允许您通过添加诸如重力,附件(弹簧)和力等行为来创建感觉真实的界面。 您可以定义您希望界面元素采用的物理特征,动态引擎负责其余部分。

Motion Effects可让您创建酷炫的视差效果,基本上,您可以利用手机加速度计提供的数据来创建对手机方向变化做出反应的界面。

在一起使用时,motiondynamics 形成了用户体验工具的强大动力,使您的数字接口变得生动。 通过让用户以自然,动态的方式响应他们的操作,您的用户将在更深层次上与您的应用程序建立联系。

UIKit动力学可以带来很多乐趣,开始学习它们的最好方法是先看一些小例子。

新建立工程,并在sb中拖进去一个控件并设置好约束。

UIKit框架(一) —— UIKit动力学和移动效果(一)_第1张图片

运行起来,如下所示的界面,这个是再平常不过的界面了,任何一个初学者都会做的一个界面。

UIKit框架(一) —— UIKit动力学和移动效果(一)_第2张图片

如果您在物理设备上运行应用,请尝试倾斜手机,将其颠倒,甚至摇晃,这些都是框架正常的设计和反应。当您向界面添加视图时,您希望它可以按照框架的定义牢固地固定到位 - 直到您为界面添加一些动态真实感!


Adding gravity - 增加重力

我们继续在上面的工程中添加代码,在viewDidLoad中添加属性。

Swift版本

var animator: UIDynamicAnimator!
var gravity: UIGravityBehavior!

这些属性是隐式解包的选项(在类型名称后面用!表示)。 这些属性必须是可选的,因为您不会在我们类的init方法中初始化它们。 您可以使用隐式解包的选项,因为我们知道在初始化它们之后这些属性不会为nil。 这可以防止您不得不使用!运算符进行手动解包。

继续添加swift代码

animator = UIDynamicAnimator(referenceView: view)
gravity = UIGravityBehavior(items: [square])
animator.addBehavior(gravity)

OC版本

下面是OC版本的,我还是直接上代码了。

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIView *dynamicView;

@end

@implementation ViewController

#pragma mark -  Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.dynamicView]];
    [animator addBehavior:gravity];
}

@end

这样写完在真机上开始运行,就会发现没有任何重力下坠的效果,这是为什么?最后检查发现就是因为animatorgravity是局部变量,当viewDidLoad执行完就销毁了,所以就没有任何重力效果了,解决方法也很简单就是将这两个对象设置为属性,让VC去持有,那么就不会销毁就有效果了,代码如下所示:

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIView *dynamicView;
@property (nonatomic, strong) UIGravityBehavior *gravity;
@property (nonatomic, strong) UIDynamicAnimator *animator;

@end

@implementation ViewController

#pragma mark -  Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    self.animator = animator;
    
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.dynamicView]];
    self.gravity = gravity;
    [animator addBehavior:gravity];
}

@end

运行效果如下所示:

UIKit框架(一) —— UIKit动力学和移动效果(一)_第3张图片

在您刚刚添加的代码中,这里有几个动态类:

  • UIDynamicAnimator是UIKit物理引擎。此类跟踪您添加到引擎的各种行为,例如重力,并提供整体上下文。在创建动画制作器的实例时,可以传入animator用来定义其坐标系的参考视图。
  • UIGravityBehavior模拟重力的行为并对一个或多个项目施加力,允许您对物理交互进行建模。创建行为实例时,将其与一组项目(通常是视图)相关联。通过这种方式,您可以选择受行为影响的项目,在这种情况下,重力影响哪些项目。

大多数行为都有许多配置属性,例如,重力行为允许您改变其角度和大小。尝试修改这些属性,使对象以不同的加速度下降,侧向或对角。

注意:关于单位的快速说明:在物理世界中,重力(g)以米/秒平方表示,大约等于9.8 m/s2。使用牛顿第二定律,您可以使用以下公式计算物体在重力影响下的落差范围,其实就是我们高中物理所学的 s=1/2gt2

distance = 0.5 × g × time2

UIKit Dynamics中,公式相同但单位不同。 而不是米,你使用每秒数千像素的单位。 使用牛顿第二定律,您仍然可以根据您提供的重力组件随时精确计算出您的视图。

你真的需要知道这一切吗? 并不是,所有你真正需要知道的是,g更大的值意味着物体会更快下降,但理解下面的数学永远不会有任何坏处,算是锦上添花吧。


Setting boundaries - 设置边界

虽然你看不到它,但即使它从屏幕底部消失,它也会继续下降。 为了使其保持在屏幕范围内,您需要定义边界。

Swift版本

var collision: UICollisionBehavior!

viewDidLoad中添加如下代码:

collision = UICollisionBehavior(items: [square])
collision.translatesReferenceBoundsIntoBoundary = true
animator.addBehavior(collision)

OC版本

@property (nonatomic, strong) UICollisionBehavior *collision;

UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.dynamicView]];
self.collision = collision;
collision.translatesReferenceBoundsIntoBoundary = YES;
[animator addBehavior:collision];

上面的代码创建了一个碰撞行为,它定义了一个或多个相关项与之交互的边界。

上述代码不是显式添加边界坐标,而是将translatesReferenceBoundsIntoBoundary属性设置为true。 这会导致边界使用提供给UIDynamicAnimator的参考视图的边界。运行,你会看到正方形与屏幕底部碰撞,弹跳一点,然后停下来,正如下所示。

UIKit框架(一) —— UIKit动力学和移动效果(一)_第4张图片

这是一个非常令人印象深刻的行为,特别是当你考虑到你在这一点上添加了多少代码时。


Handling collisions - 处理碰撞

接下来你将添加一个不可移动的障碍,落下的方块将碰撞和相互作用。将以下代码插入viewDidLoad中将方块视图添加到视图的行之后。

Swift版本

let barrier = UIView(frame: CGRect(x: 0, y: 300, width: 130, height: 20))
barrier.backgroundColor = UIColor.redColor()
view.addSubview(barrier)

OC版本

首先是在sb中拖进去一个barrier障碍的UIView控件,然后设置约束。

UIKit框架(一) —— UIKit动力学和移动效果(一)_第5张图片

然后我们运行,看这个barrier是否起到了障碍的作用。

UIKit框架(一) —— UIKit动力学和移动效果(一)_第6张图片

你会看到一个红色的“障碍”延伸到屏幕的中间。 然而,事实证明,当方块视图时直接穿过障碍时,障碍不是那么有效。

这并不是你想要的效果,但确实提供了一个重要的提醒:dynamics只会影响与行为相关的视图。

看一下下面这个示意图

UIKit框架(一) —— UIKit动力学和移动效果(一)_第7张图片

上面的Red Barrier就是红色的障碍,Square就是我们的方块视图。

UIDynamicAnimator与提供坐标系的参考视图相关联。 然后添加一个或多个行为,对与其关联的item施加力。 大多数行为可以与多个item相关联,并且每个item可以与多个行为相关联。 上图显示了您应用中的当前行为及其关联。

当前代码中的任何行为都没有“意识到”障碍那个视图,因此就底层动力学引擎而言,障碍甚至不存在。


Making objects respond to collisions - 使对象响应collisions

要使方块视图与屏障barrier发生碰撞,请找到初始化碰撞行为的行并将其替换为以下内容:

Swift版本

collision = UICollisionBehavior(items: [square, barrier])

OC版本

@property (weak, nonatomic) IBOutlet UIView *barrierView;

UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.dynamicView, self.barrierView]];

也就是说碰到行为影响到的数组参数中添加self.barrierView,这样才会给这个视图添加碰撞行为。

下面我们就看一下实际运行效果。

UIKit框架(一) —— UIKit动力学和移动效果(一)_第8张图片

碰撞行为在与其相关联的每个item周围形成一个boundary,这会将它们从可以相互传递的对象变为更加坚固的东西。

更新前面的图表,您可以看到碰撞行为现在与两个视图相关联:

UIKit框架(一) —— UIKit动力学和移动效果(一)_第9张图片

但是,两个对象之间的交互仍然存在一些不太恰当的问题。 障碍物应该是不可移动的,但当两个物体在你当前的配置中发生碰撞时,障碍物会被撞掉并开始向屏幕底部旋转。

更奇怪的是,障碍物从屏幕底部反弹并且不像方块视图那样稳定下来 - 这是有道理的,因为重力行为不会与屏障相互作用。 这也解释了为什么屏障在正方形与之碰撞之前不会移动。

看起来您需要一种不同的方法来解决问题。 由于屏障视图是不可移动的,因此dynamics引擎不需要知道它的存在。 但是如何检测到碰撞?

限于篇幅,下一篇继续~~~

参考文章

1. iOS7 UIKit动力学-重力特性UIGravityBehavior
2. Objective-C学习之UIGravityBehavior仿真行为(重力、碰撞)
3. UIGravityBehavior isn't working

后记

本篇主要讲述了UIKit动力学和移动效果,感兴趣的给个赞或者关注~~~~

UIKit框架(一) —— UIKit动力学和移动效果(一)_第10张图片

你可能感兴趣的:(UIKit框架(一) —— UIKit动力学和移动效果(一))