UIKit框架(一) —— UIKit動(dòng)力學(xué)和移動(dòng)效果(一)

版本記錄

版本號(hào) 時(shí)間
V1.0 2018.08.05

前言

iOS中有關(guān)視圖控件用戶能看到的都在UIKit框架里面,用戶交互也是通過UIKit進(jìn)行的,這一篇就看一下UIKit動(dòng)力學(xué)和移動(dòng)效果。

簡(jiǎn)介

UIKit Dynamics是集成到UIKit的完整物理引擎。 它允許您通過添加諸如重力,附件(彈簧)和力等行為來創(chuàng)建感覺真實(shí)的界面。 您可以定義您希望界面元素采用的物理特征,動(dòng)態(tài)引擎負(fù)責(zé)其余部分。

Motion Effects可讓您創(chuàng)建酷炫的視差效果,基本上,您可以利用手機(jī)加速度計(jì)提供的數(shù)據(jù)來創(chuàng)建對(duì)手機(jī)方向變化做出反應(yīng)的界面。

在一起使用時(shí),motiondynamics 形成了用戶體驗(yàn)工具的強(qiáng)大動(dòng)力,使您的數(shù)字接口變得生動(dòng)。 通過讓用戶以自然,動(dòng)態(tài)的方式響應(yīng)他們的操作,您的用戶將在更深層次上與您的應(yīng)用程序建立聯(lián)系。

UIKit動(dòng)力學(xué)可以帶來很多樂趣,開始學(xué)習(xí)它們的最好方法是先看一些小例子。

新建立工程,并在sb中拖進(jìn)去一個(gè)控件并設(shè)置好約束。

運(yùn)行起來,如下所示的界面,這個(gè)是再平常不過的界面了,任何一個(gè)初學(xué)者都會(huì)做的一個(gè)界面。

如果您在物理設(shè)備上運(yùn)行應(yīng)用,請(qǐng)嘗試傾斜手機(jī),將其顛倒,甚至搖晃,這些都是框架正常的設(shè)計(jì)和反應(yīng)。當(dāng)您向界面添加視圖時(shí),您希望它可以按照框架的定義牢固地固定到位 - 直到您為界面添加一些動(dòng)態(tài)真實(shí)感!


Adding gravity - 增加重力

我們繼續(xù)在上面的工程中添加代碼,在viewDidLoad中添加屬性。

Swift版本

var animator: UIDynamicAnimator!
var gravity: UIGravityBehavior!

這些屬性是隱式解包的選項(xiàng)(在類型名稱后面用!表示)。 這些屬性必須是可選的,因?yàn)槟粫?huì)在我們類的init方法中初始化它們。 您可以使用隱式解包的選項(xiàng),因?yàn)槲覀冎涝诔跏蓟鼈冎筮@些屬性不會(huì)為nil。 這可以防止您不得不使用!運(yùn)算符進(jìn)行手動(dòng)解包。

繼續(xù)添加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

這樣寫完在真機(jī)上開始運(yùn)行,就會(huì)發(fā)現(xiàn)沒有任何重力下墜的效果,這是為什么?最后檢查發(fā)現(xiàn)就是因?yàn)?code>animator和gravity是局部變量,當(dāng)viewDidLoad執(zhí)行完就銷毀了,所以就沒有任何重力效果了,解決方法也很簡(jiǎn)單就是將這兩個(gè)對(duì)象設(shè)置為屬性,讓VC去持有,那么就不會(huì)銷毀就有效果了,代碼如下所示:

#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

運(yùn)行效果如下所示:

在您剛剛添加的代碼中,這里有幾個(gè)動(dòng)態(tài)類:

  • UIDynamicAnimator是UIKit物理引擎。此類跟蹤您添加到引擎的各種行為,例如重力,并提供整體上下文。在創(chuàng)建動(dòng)畫制作器的實(shí)例時(shí),可以傳入animator用來定義其坐標(biāo)系的參考視圖。
  • UIGravityBehavior模擬重力的行為并對(duì)一個(gè)或多個(gè)項(xiàng)目施加力,允許您對(duì)物理交互進(jìn)行建模。創(chuàng)建行為實(shí)例時(shí),將其與一組項(xiàng)目(通常是視圖)相關(guān)聯(lián)。通過這種方式,您可以選擇受行為影響的項(xiàng)目,在這種情況下,重力影響哪些項(xiàng)目。

大多數(shù)行為都有許多配置屬性,例如,重力行為允許您改變其角度和大小。嘗試修改這些屬性,使對(duì)象以不同的加速度下降,側(cè)向或?qū)恰?/p>

注意:關(guān)于單位的快速說明:在物理世界中,重力(g)以米/秒平方表示,大約等于9.8 m/s2。使用牛頓第二定律,您可以使用以下公式計(jì)算物體在重力影響下的落差范圍,其實(shí)就是我們高中物理所學(xué)的 s=1/2gt2

distance = 0.5 × g × time2

UIKit Dynamics中,公式相同但單位不同。 而不是米,你使用每秒數(shù)千像素的單位。 使用牛頓第二定律,您仍然可以根據(jù)您提供的重力組件隨時(shí)精確計(jì)算出您的視圖。

你真的需要知道這一切嗎? 并不是,所有你真正需要知道的是,g更大的值意味著物體會(huì)更快下降,但理解下面的數(shù)學(xué)永遠(yuǎn)不會(huì)有任何壞處,算是錦上添花吧。


Setting boundaries - 設(shè)置邊界

雖然你看不到它,但即使它從屏幕底部消失,它也會(huì)繼續(xù)下降。 為了使其保持在屏幕范圍內(nèi),您需要定義邊界。

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];

上面的代碼創(chuàng)建了一個(gè)碰撞行為,它定義了一個(gè)或多個(gè)相關(guān)項(xiàng)與之交互的邊界。

上述代碼不是顯式添加邊界坐標(biāo),而是將translatesReferenceBoundsIntoBoundary屬性設(shè)置為true。 這會(huì)導(dǎo)致邊界使用提供給UIDynamicAnimator的參考視圖的邊界。運(yùn)行,你會(huì)看到正方形與屏幕底部碰撞,彈跳一點(diǎn),然后停下來,正如下所示。

這是一個(gè)非常令人印象深刻的行為,特別是當(dāng)你考慮到你在這一點(diǎn)上添加了多少代碼時(shí)。


Handling collisions - 處理碰撞

接下來你將添加一個(gè)不可移動(dòng)的障礙,落下的方塊將碰撞和相互作用。將以下代碼插入viewDidLoad中將方塊視圖添加到視圖的行之后。

Swift版本

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

OC版本

首先是在sb中拖進(jìn)去一個(gè)barrier障礙的UIView控件,然后設(shè)置約束。

然后我們運(yùn)行,看這個(gè)barrier是否起到了障礙的作用。

你會(huì)看到一個(gè)紅色的“障礙”延伸到屏幕的中間。 然而,事實(shí)證明,當(dāng)方塊視圖時(shí)直接穿過障礙時(shí),障礙不是那么有效。

這并不是你想要的效果,但確實(shí)提供了一個(gè)重要的提醒:dynamics只會(huì)影響與行為相關(guān)的視圖。

看一下下面這個(gè)示意圖

上面的Red Barrier就是紅色的障礙,Square就是我們的方塊視圖。

UIDynamicAnimator與提供坐標(biāo)系的參考視圖相關(guān)聯(lián)。 然后添加一個(gè)或多個(gè)行為,對(duì)與其關(guān)聯(lián)的item施加力。 大多數(shù)行為可以與多個(gè)item相關(guān)聯(lián),并且每個(gè)item可以與多個(gè)行為相關(guān)聯(lián)。 上圖顯示了您應(yīng)用中的當(dāng)前行為及其關(guān)聯(lián)。

當(dāng)前代碼中的任何行為都沒有“意識(shí)到”障礙那個(gè)視圖,因此就底層動(dòng)力學(xué)引擎而言,障礙甚至不存在。


Making objects respond to collisions - 使對(duì)象響應(yīng)collisions

要使方塊視圖與屏障barrier發(fā)生碰撞,請(qǐng)找到初始化碰撞行為的行并將其替換為以下內(nèi)容:

Swift版本

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

OC版本

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

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

也就是說碰到行為影響到的數(shù)組參數(shù)中添加self.barrierView,這樣才會(huì)給這個(gè)視圖添加碰撞行為。

下面我們就看一下實(shí)際運(yùn)行效果。

碰撞行為在與其相關(guān)聯(lián)的每個(gè)item周圍形成一個(gè)boundary,這會(huì)將它們從可以相互傳遞的對(duì)象變?yōu)楦訄?jiān)固的東西。

更新前面的圖表,您可以看到碰撞行為現(xiàn)在與兩個(gè)視圖相關(guān)聯(lián):

但是,兩個(gè)對(duì)象之間的交互仍然存在一些不太恰當(dāng)?shù)膯栴}。 障礙物應(yīng)該是不可移動(dòng)的,但當(dāng)兩個(gè)物體在你當(dāng)前的配置中發(fā)生碰撞時(shí),障礙物會(huì)被撞掉并開始向屏幕底部旋轉(zhuǎn)。

更奇怪的是,障礙物從屏幕底部反彈并且不像方塊視圖那樣穩(wěn)定下來 - 這是有道理的,因?yàn)橹亓π袨椴粫?huì)與屏障相互作用。 這也解釋了為什么屏障在正方形與之碰撞之前不會(huì)移動(dòng)。

看起來您需要一種不同的方法來解決問題。 由于屏障視圖是不可移動(dòng)的,因此dynamics引擎不需要知道它的存在。 但是如何檢測(cè)到碰撞?

限于篇幅,下一篇繼續(xù)~~~

參考文章

1. iOS7 UIKit動(dòng)力學(xué)-重力特性UIGravityBehavior
2. Objective-C學(xué)習(xí)之UIGravityBehavior仿真行為(重力、碰撞)
3. UIGravityBehavior isn't working

后記

本篇主要講述了UIKit動(dòng)力學(xué)和移動(dòng)效果,感興趣的給個(gè)贊或者關(guān)注~~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容