Autolayout約束動畫化(賣什么萌?。?/h1>
- 原文:Animating Autolayout Constraints
- 作者:@kharrison
- 譯者:CocoaChina--起個名字好難(CC論壇ID)
記于二零一五年晚春:距上次發(fā)文已有五天,看了眾同僚的反饋,我把這段代碼重新搞了搞,以此來避免在運行時添加或刪除約束。取代這些像JAVA一樣笨重的方法的是我開始動態(tài)的改變黃色和藍色視圖約束的優(yōu)先級。超級簡單,超級高效。
在沒有autoLayout之前,如果你想移動一個view,frame就會派出他兩個小弟origin或者size來對你大喊@"你動我下試試!"。當(dāng)然,frame、bounds和center都是UIView的屬性,所以當(dāng)你真的動了他們,UIView是不會作罷甘休的。所以你知道為啥用戶能看到一系列的動畫了吧,UIView想削你呢。
如果你開始用autoLayout了,很快你就發(fā)現(xiàn)你不用親手去處理frame(或者bounds或者center)了。你可以叫約束去搞他。這篇文章就是通過一個簡單的栗子,來告訴你怎么用約束搞點事情出來。額,確切的說是搞出一個簡單的動畫效果。
挑戰(zhàn)
為了簡單明了,我們只用兩個視圖。一個黃色視圖(以下簡稱黃圖)和一個藍色視圖(以下簡稱藍圖)。在"普通"模式下,我們只看到黃圖。在"五彩繽紛"模式下,藍色和黃色視圖都能看到。
視圖…
"喂!為什么都只看到黃圖?。?quot;
"明明只有兩種顏色叫五彩繽紛什么的真的好嗎!"
"你倒是留個種子??!"
啊,不好意思,光注意黃圖了。在"五彩繽紛"模式下,我們看到黃圖和藍圖兩個。這倆視圖應(yīng)該填滿整個屏幕,除了設(shè)備的邊邊以及switch占據(jù)的框框。下面這個gif就是我們想去實現(xiàn)的效果。
藍圖應(yīng)該在右邊滑出,然后黃圖相應(yīng)地充斥整個屏幕…
設(shè)置基本約束
一開始,我在IB上拖出視圖,拉上約束。這個時候倆視圖都是可見的。
黃圖有五個約束:左邊相對父視圖間隔,右邊相對藍圖間隔,上邊相對switch間隔,下邊相對父視圖間隔,以及和藍圖寬度相等約束。
藍圖和黃圖的約束差不多,除了藍圖是右邊相對父視圖間隔。
非必需約束優(yōu)先級
在只有黃圖可見的時候(真是不錯),我們需要加另一個約束,也就是它右側(cè)相對父視圖的間隔約束。如果在上面我加上這個約束,那么他就和那個"右側(cè)相對藍圖約束"沖突了,因為他倆同時有優(yōu)先級1000。為了避免沖突以及移動藍圖,我們可以改變一下黃藍圖間隔的那個約束的優(yōu)先級。
必需約束優(yōu)先級是這個UILayoutPriorityRequired(1000),你不能在運行時改變一個必需約束的優(yōu)先級。優(yōu)先級比UILayoutPriorityRequired小的,就是一個可選或者非必需的約束,類似這種,只要你別把優(yōu)先級設(shè)置為UILayoutPriorityRequired,你就可以改。
所以首先,我們把藍圖右側(cè)相對父視圖約束的優(yōu)先級搞低一點,搞到750.
然后我們在給黃圖加一個它右側(cè)相對父視圖的約束(就像上面提到的),優(yōu)先級也搞到750.
把約束拖出來!
為了在運行時改變藍圖右側(cè)約束我們得先把這個約束拖到代碼中。你也可以像這樣拖任意的約束出來。(就像把控件關(guān)聯(lián)到代碼中一樣,選中約束,按Ctrl拖)
- @property (weak, nonatomic) IBOutlet NSLayoutConstraint *blueViewConstraint;
為了確保我們把藍圖推出屏幕,我們也得調(diào)整黃圖和藍圖中間的間隔約束,所以我們把這個約束也整到代碼中。
- @property (weak, nonatomic) IBOutlet NSLayoutConstraint *viewSpacingContraint;
更新約束
- - (void)updateConstraintsForMode {
- if (self.modeSwitch.isOn) {
- self.viewSpacingContraint.constant = 8.0;
- self.blueViewConstraint.priority = UILayoutPriorityDefaultHigh+1;
- } else {
- self.viewSpacingContraint.constant = self.view.frame.size.width;
- self.blueViewConstraint.priority = UILayoutPriorityDefaultHigh-1;
- }
- }
現(xiàn)在可以很容易的寫一個方法來根據(jù)模式開關(guān)設(shè)置藍圖約束想要的優(yōu)先級。
我們在storyboard中把黃圖右側(cè)相對父視圖的約束也設(shè)定了優(yōu)先級UILayoutPriorityDefaultHigh(750)。為了使藍圖可見,我們需要把藍圖的右側(cè)約束優(yōu)先級設(shè)定的比750高一些,而隱藏起藍圖時我們得把它設(shè)定的低一些。
請注意!看黑板!我們要給黃藍圖的間隔設(shè)定一個大點的值(我這里用的屏幕寬度)以確保藍圖推出右側(cè)邊界。
我們在視圖第一次加載時也應(yīng)該配置下約束。厚此薄彼可不好。
- - (void)viewDidLoad {
- // ...
- [self updateConstraintsForMode];
- }
動起來!
現(xiàn)在萬事俱備只欠東風(fēng)了,我們現(xiàn)在只需要輕輕的撥動一下模式開關(guān),咦?輕輕的,咦?啊不好意思,忘記把開關(guān)的事件代碼寫上了- -,蘋果的Auto Layout Guide描述了autoLayout搞動畫的基本方法,推薦的代碼如下:
- [containerView layoutIfNeeded];
- [UIView animateWithDuration:1.0 animations:^{
- // Make all constraint changes here
- [containerView layoutIfNeeded];
- }];
這兩個對layoutIfNeeded的調(diào)用強迫將要執(zhí)行的操作完成,然后在動畫塊里捕獲frame的改變。
(譯者補充:想起知乎里一個回答 你在公司項目里面看到過哪些操蛋的代碼?)
- if (m_doc->isModified() == true)
- {
- for (int i = 0; i < 100; i++)
- {
- save(); //Save the document for 100 times to ensure it has been saved successfully.
- }
- }
在我們的栗子中用上面的方法,就是這樣式的:
- - (IBAction)enableMode:(UISwitch *)sender {
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- [defaults setBool:sender.isOn forKey:modeUserDefaultKey];
- [defaults synchronize];
- [self.view layoutIfNeeded];
- [UIView animateWithDuration:1.0 animations:^{
- [self updateConstraintsForMode];
- [self.view layoutIfNeeded];
- }];
- }
總結(jié)
代碼在這,點我點我。