Gallery[39367:a0b] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) ( "<NSLayoutConstraint:0xc1a1e80 V:[UIView:0xc1a2b10(284)]>", "<NSLayoutConstraint:0xc1a36c0 V:[_UILayoutGuide:0xc1a2d20]-(65)-[UIView:0xc1a2b10]>", "<NSLayoutConstraint:0xc1a36f0 V:[UIView:0xc1a2b10]-(199)-[_UILayoutGuide:0xc1a3230]>", "<_UILayoutSupportConstraint:0xc15dbd0 V:[_UILayoutGuide:0xc1a2d20(20)]>", "<_UILayoutSupportConstraint:0xc1a1510 V:|-(0)-[_UILayoutGuide:0xc1a2d20] (Names: '|':UIView:0xc1a2930 )>", "<_UILayoutSupportConstraint:0xc1a3720 V:[_UILayoutGuide:0xc1a3230(0)]>", "<_UILayoutSupportConstraint:0xc1a30e0 _UILayoutGuide:0xc1a3230.bottom == UIView:0xc1a2930.bottom>", "<NSAutoresizingMaskLayoutConstraint:0x8c6c6a0 h=--& v=--& H:[UIView:0xc1a2930(320)]>" ) Will attempt to recover by breaking constraint <NSLayoutConstraint:0xc1a1e80 V:[UIView:0xc1a2b10(284)]> Break on objc_exception_throw to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful. . . . |
你還記得我說過“必須有足夠的約束,自動佈局才能計算所有視圖的位置和尺寸”麼?好吧,在這個例子中,約束又太多了。當你看到“不能同時滿足所有約束(Unable to simultaneously satisfy constraints)”的錯誤的時候,那就意味着你的約束之間有衝突。
讓我們再看一下哪些約束:
綠色視圖上設置了6個約束,4個間距約束(1-4)和你剛剛設置的寬高約束(5和6)。衝突在哪呢?
在豎屏的時候不應該有問題,因爲在數學上是滿足的。父視圖的寬是320點。如果你添加了Horizontal Space和Width約束,你應該保證它們的總長度小於等於320點。這是我計算視圖位置的方式:98+160+62 = 320。同樣的,所有豎向約束相加以後就應該是568點。
但是當你旋轉設備爲橫屏時,窗口(指的父視圖)就變成了568點寬。這意味着98+160+62+? = 568。這裏額外多了248點,無法滿足方程式,所以自動佈局不知道從哪裏來得到這248點。同樣的,對於豎向約束也是一樣。
解決衝突的辦法有兩個:第一是保持view的寬度固定,但外邊距必須可變。第二是保持外邊距固定,但寬度必須可變。你不能同時固定它們。你要去掉這些約束中的其中一個。在上面的例子中,若你想要在橫屏豎屏中都擁有同樣的寬,那Horizontal Space就必須去掉。
移除右邊的橫向間距和下邊豎向間距。故事板上會顯示成這樣:
好了,現在view就有一個正確約束來預判它的尺寸與位置了 - 不多,不少。運行app來查證一下錯誤信息是否已經沒有了,然後這個view在旋轉以後是否能保持寬度。
注意:儘管Interface Builder可以很好的警告你有無效的約束,但它不是神。它只會警告你:噢,你的約束不夠。但是如果你約束太多了,它就無法檢測出來了。不過至少在出錯的時候,自動佈局還是會給出一個詳細的錯誤信息。如果你想要學習更多關於如何分析錯誤信息和診斷佈局問題,那你可以看 iOS 6 by Tutorials中的“Intermediate Auto Layout”。
豎屏繪製
拖拽一個label到綠色的view上。注意看,現在參考線出現在了綠色view的內部,因爲它是label的父視圖。
調整label的位置到參考線的下邊距,橫向居中的地方。給label下面添加與綠色view之間的間距約束,有20點的距離。快速的方式是使用Pin按鈕,僅選擇下面的T字架:
現在給label添加橫向居中的約束。你已經試過Editor\Align菜單了,但你也可以使用自動佈局菜單的Align按鈕。選擇label並點擊Align按鈕,得到一個彈出窗:
在Horizontal Center前打上勾然後點擊Add 1 Constraint。這時候,故事板應該是這個樣子的:
注意這兩個新的橫向和縱向間距約束是位於綠色view自己的約束列表裏,而不是在主視圖裏。
拖拽一個新的Image View對象到故事板上,讓你的佈局看起來像這樣::
這個圖片視圖固定了上,左,右邊緣在父視圖上,但下部以標準的8點間距連接在了label的頂部。如果你不確定要怎麼做,那就跟着下面的步驟走:
1. 拖拽image view到綠色視圖中,現在不用擔心它的尺寸和位置:
2. 選中image view,按Pin按鈕選擇下面的選項:
上,左,右的T字架設置爲20點,但是下面的設置爲8點。重點:對於Update Frames,你應該選擇Items of New Constraints。如果你左邊距已經默認滿足要求,故事板看起來就是這樣的:
上面這個約束是你選擇了一個不一樣的frame。如果你選擇了Items of New Constraints, Interface Builder將自動的調整畫面來添加約束,一切看起來都很棒:
當然,如果你選錯了frame,你也可以使用Resolve Auto Layout Issues button來修復它:
下載這個教程資源並解壓這個文件。你就會找到一個圖片文件夾 - 添加這個文件夾到你的項目。設置Ray.png作爲這個image view的圖片,改變image view的模式爲Aspect Fit並且設置它的背景色爲白色。把label的文本改爲“Ray”。
你的佈局應該是這樣的:
你可能注意到綠色view內部的約束突然變成了橘黃色。這發生在你給image view設置圖片的時候。你的佈局爲什麼突然無效了?幸運的是你可以帶着你的猜想來讓Xcode告訴你爲什麼錯處了。
點擊這個這個紅色的靶子,它位於View控制器的Outline文檔中:
你會看到一個Content Priority Ambiguity(內容優先級歧義)的錯誤。這意味着:如果image view和label都沒有固定的高度,那自動佈局系統就不知道應該怎麼分配大小。(Interface Builder似乎會忽略已經設置過的固定高度約束)
讓我們把綠色的view的高度變爲100點試試。自動佈局是怎麼將這100點分配給view內部的label和image view的?是label保持尺寸而image view變成100點高了麼?還是label變高了而image view保持尺寸?還是他們各自被分配了50點,又或者劃分成25/75,40/60,又或者其它的什麼結合?
如果你不解決這個問題,那麼自動佈局系統就會去猜測,就可能導致結果莫名其妙。
恰當的解決辦法是改變label的“Content Compression Resistance Priority”。你將從隨後內容中學習到更多。現在,打開label的Size inspector,設置Content Compression Resistance Priority的vertical爲751。這樣就使它的優先級高於image view。然後繼續設置Content Hugging Priority爲252。
這時候T字架就會再次變成藍色,自動佈局系統的警告也會消失。
添加到另一個頂端
拖拽綠色的view到主視圖的左上角。回憶一下之前做過的,綠色的view已經有Horizontal Space和Vertical Space約束來控制它在父視圖中的位置了。現在,這些約束依然存在,它們導致了視圖的frame無法對期喎�"http://www.it165.net/design/wrss/" target="_blank" class="keylink">rss6/vM/foaM8L3A+CjxwPjxpbWcgc3R5bGU9"display: block; margin-left: auto; margin-right: auto;" src="http://www.it165.net/uploadfile/files/2014/0920/2014092019054458.png" alt="" />
爲了修正它,使用Resolve Auto Layout Issues按鈕並選擇Update Constraints。之前你使用的Update Frames,用來移動和重定義view的尺寸來匹配你的約束。現在這剛好是一個相反的操作:你會用你的約束來匹配view的frame。
你可能注意到在頂部的Vertical Space現在是負的。這種情況是因爲約束鏈接到了Top Layout Guide(頂部參考線)。不要問爲什麼,沒有規定說約束值不能是負的,你可以就像這樣留下它。(如果你看着礙眼,那就刪掉 “Vertical Space (-20)”約束,然後把view重新綁定到窗口的頂部)
Horizontal Space現在爲0,表現爲一條緊貼窗口左邊緣的粗藍線。儘管view已經位於角落裏了,但是它任然需要約束來固定住它:
選擇綠色的視圖按下?D來複制它。移動複製的視圖到右上角:
注意T字架現在是橘黃色的。當你複製它的時候,它已經上丟失了X,Y座標的約束。爲了修正它,固定view到窗口的右、上邊緣。
再複製兩次,分別把複製的視圖放在坐下角落和右下角落。然後再把他們固定在他們該在地方。
改變後的場景如下:
哈哈~他們看起來好像都是程序員 :-)
運行app,在豎屏上很棒,但是橫屏上可能不是那麼好:
本來應該很棒的戰士卻變糟了:你給4個色彩鮮豔的容器view設定了固定的寬和高,因此它們就總是保證這樣的尺寸,而不管它們的父視圖的尺寸如何。
從這4個view下,選擇Width (160)和Height (284)的約束並刪除它們(在Outline文檔中很容易完成)。如果你現在運行app,你會看到:
注意:如果你很奇怪爲什麼有的view變得比其他的大了,那我告訴你,這是固有內容尺寸的原因。圖片的尺寸決定了image view有多大。文本的尺寸決定了label有多大。再加上四邊的20點外邊距,所有的尺寸加起來決定了每個視圖的尺寸。
這看起來很像你的上一部分解決的問題,因此你要向前思考,你可能回憶起來了,你要讓每個view的寬和高都相等。
選擇這4個view。Outline大綱裏很容易完成;按住?點擊這4個view。你就可以添加這個約束。在彈出窗中,給Equal Widths和Equal Heights打上勾,然後點擊Add 6 Constraints。
再次運行這個app並且旋轉設備。噢...還是不咋地:
所有的view都有同樣的高,並且他們也有同樣的寬,你的約束是對的。但這個寬和高可能不是你想要的。
光告訴自動佈局系統必須有同樣的尺寸是不夠的。因爲自動佈局系統不炸ky"http://www.it165.net/qq/" target="_blank" class="keylink">qq1wNXiNLj2dmlld8rHu6XP4MGsvdO1xKGjy/zDx7XEsd/M+bHftcTJ6LzGo6y1q9Tay/zDx9auvOTDu9PQ1eLR+bXE1LzK+KGj19S2r7K8vta+zbK71qq1wMv80OjSqtTaJmxkcXVvO1JheSZyZHF1bzu49iZsZHF1bztNYXR0aGlqcyZyZHF1bzvWrrzku6631rSwv9qhozwvcD4KPHA+yOe5+9fUtq+yvL7Wz7XNs9fUvLqyu8TczeqzyaOsxMfE477N0qq45svfy/yhozwvcD4KPHA+PGltZyBzdHlsZT0="display: block; margin-left: auto; margin-right: auto;" src="http://www.it165.net/uploadfile/files/2014/0920/2014092019054675.png" alt="" />
選擇Ray和Matthijs的視圖,從編輯菜單選擇Pin\Horizontal Spacing。因爲view之間是邊貼邊的,那在它們之間就要加上尺寸爲0的Horizontal Space約束。好了,現在有足夠的約束讓自動佈局系統瞭解兩個view之間的關係了。再給Ray和Dennis Ritchie視圖之間使用Editor\Pin\Vertical Spacing。
再運行app,現在看起來就像這樣:
注意:Interface Builder任然會抱怨view的位置不對。我不確信爲什麼會發生這種情況,可能是Xcode有bug吧。如果這些警告信息讓你不爽,那就選擇主視圖(或view控制器)並且從Resolve Auto Layout Issues菜單選擇Update All Frames in View Controller。不過你的app運行時這種改變不會起作用,但至少讓Xcode看起來很舒心。
image view上有一點要注意:它們可能會延伸了,因爲你沒有給它們一個固定尺寸。你可能不知道,這是有意這樣做的。image view不能適配橫屏的模式。如果你想保持image view的原始寬高比,那很不幸。在Interface Builder中你是無法得到下面這樣的效果的:
不幸的是,Interface Builder不能正確的提供用約束保持view的原樣寬高比。如果要這麼做,你就需要自己編程。你可以在iOS 6 by Tutorials的“Intermediate Auto Layout”中學習如何操作。
我該去哪繼續學習?
如果你全都看完做完了,祝賀你 - 你現在知道自動佈局是什麼了,並且有了一定的實踐基礎!但你任然有許多需要學習...
這個教程的第一部分你可以從iOS 6 by Tutorials一書中的自動佈局章節閱讀到。第二部分會教你如何使用自動佈局來創建更多“真實世界”的場景佈局,你可以從Interface Builder學習一切你想知道的自動佈局的知識。
但是就像其他的視覺設計工具一樣,Interface Builder也有自己的侷限性。有時,它只能通過NSLayoutConstraint對象來從代碼實現。IOS 6的教程有一個章節包含了這個主題所有的內容,Intermediate Auto Layout。因此,如果你想知道自動佈局的後半部分,請買這本書吧!(翻譯到最後,發現居然帶廣告,爲了尊重原作者,還是給出連接吧~~~)