2013-09-25 45 views
33

我在xcode 5項目中遇到了自動佈局問題。我正在使用導航控制器內部的普通視圖控制器。上半部分有MKMapView,下半部分有UITableView。我正在使用storyboards,並配置了原型UITableViewCell,但我通過代碼添加了約束。我已經仔細檢查了原型中的每個控件,並且沒有看到在此處配置的任何約束。當我爲UITableViewCell添加約束時,會出現問題。我在單元格中有以下代碼:UITableViewCell上的AutoLayout問題

-(void)updateConstraints { 
    [super updateConstraints]; 
    //first remove old constraints 
    [self removeConstraints:self.constraints]; 
    [self.nameLabel removeConstraints:self.nameLabel.constraints]; 
    [self.addressLabel removeConstraints:self.nameLabel.constraints]; 
    [self.rentableSquareFeetLabel removeConstraints:self.rentableSquareFeetLabel.constraints]; 
    [self.lastSaleAmountLabel removeConstraints:self.lastSaleAmountLabel.constraints]; 
    [self.lastSaleDateLabel removeConstraints:self.lastSaleAmountLabel.constraints]; 
    [self.thumbnailImageView removeConstraints:self.thumbnailImageView.constraints]; 

    //then set up constraints 
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_thumbnailImageView, _nameLabel, _rentableSquareFeetLabel, _lastSaleAmountLabel, _addressLabel, _lastSaleDateLabel); 
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_thumbnailImageView(60)]-[_nameLabel(<=200)]-(>=8)-[_rentableSquareFeetLabel]-(>=8)-[_lastSaleAmountLabel]|" options:0 metrics:nil views:viewsDictionary]]; 
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_nameLabel]-(-4)-[_addressLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]]; 
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_lastSaleAmountLabel]-(-4)-[_lastSaleDateLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:viewsDictionary]]; 
} 

我在調試控制檯中獲得以下代碼。該異常由第一個addConstraints行觸發。如果我只是繼續通過這些那麼最終一切都顯示爲它應該是,因爲它看起來像Xcode是選擇打破了正確的約束:

2013-09-25 15:07:14.169 PECProperties[32381: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) (
    "<NSIBPrototypingLayoutConstraint:0x9d56c70 'IB auto generated at build time for view with fixed frame' H:|-(0)-[UIImageView:0x9d558f0](LTR) (Names: '|':UITableViewCellContentView:0x9d55620)>", 
    "<NSIBPrototypingLayoutConstraint:0x9d56d20 'IB auto generated at build time for view with fixed frame' H:[UIImageView:0x9d558f0(60)]>", 
    "<NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR) (Names: '|':UITableViewCellContentView:0x9d55620)>", 
    "<NSLayoutConstraint:0x9d53830 H:[UIImageView:0x9d558f0]-(NSSpace(8))-[UILabel:0x9d559e0]>") 

Will attempt to recover by breaking constraint <NSIBPrototypingLayoutConstraint:0x9d56d80 'IB auto generated at build time for view with fixed frame' H:|-(78)-[UILabel:0x9d559e0](LTR) (Names: '|':UITableViewCellContentView:0x9d55620)> 

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. 

第三NSIBPrototypingLayoutConstraint顯示視圖的邊緣和之間的78點標籤。這就是原型定位的粗略位置(如果我在原型中移動它,我會看到調試控制檯中約束的變化),但這與我自己的圖像視圖和標籤之間的「標準」距離限制相沖突。

我已經嘗試在視圖控制器的cellForRowAtIndexPath中設置translatesAutoresizingMaskIntoConstraints=NO,但這似乎也沒有幫助。我如何修復佈局?

+0

我注意到iOS7中的一個錯誤導致了類似的問題。我在IB中指定了所有約束條件,XCode很高興。但是異常<單元格高度的所有垂直尺寸的總和。解決方案是可以接受的 - 在構建時刪除其中的一個約束條件。 – Matt

回答

78

有幾件事情在這裏介紹:

  1. 你正在運行到(以及正在造成的例外)是自動生成的由Interface Builder,以使您的故事板或XIB視圖NSIBPrototypingLayoutConstraint約束佈局不含糊。這樣做很偷偷摸摸,但它會自動添加所需的最小約束,以便每個不明確的視圖的位置和大小都被完全指定。這是從Xcode 4的變化,因爲在Xcode 4中,您不能在Interface Builder中使用不明確的佈局。使用Xcode 5及更高版本可以,但是如果佈局在編譯時不明確,IB將自動爲您生成這些約束。

    解決此問題的方法是在界面生成器中添加所需的最少約束條件,以便每個視圖的位置&大小已完全指定,然後選擇每個不需要的約束,轉到右側邊欄屬性檢查器,並檢查佔位符 - 在製造時刪除

    Screenshot of the constraint Placeholder checkbox in Xcode 6.

    這不僅複選框刪除您添加的約束,但最重要的是它會阻止自動生成IB約束採取它的地方! (正如你可以想象的,當你在IB中有很多視圖並且想要管理你在代碼中的所有約束時,這是非常乏味的。因此,你可能想要避免完全使用IB來實現Auto的視圖層次結構以編程方式佈局。)

    佔位符約束與卸載約束之間的區別是什麼?下面是從我Adaptive Auto Layout talk (video)PDF slides)比較兩個幻燈片:

    Comparison between Placeholder constraints and Uninstalled constraints.

  2. updateConstraints,你不想刪除約束和像你有沒有重新添加。爲什麼不?從本質上來說,這對性能來說是非常糟糕的,我已經向蘋果工程師證實這不是一個好主意。有關更多詳細信息,請參閱question/answer I have posted here以及此answer。爲了防止約束被多次添加,使用布爾標誌(例如hasSetupConstraints),一旦您第一次設置了約束條件,就將其設置爲YES,並且如果再次調用updateConstraints,則可以立即返回,如果沒有新的約束添加。進一步討論見this question

  3. 您用於刪除約束的代碼可能無法完全運行。這是因爲[view removeConstraints:view.constraints]只會刪除已添加到view的約束 - 請記住,約束可以添加到它們約束視圖的任何常見超視圖 - 並且添加到view的約束可能不是唯一影響view的佈局的約束!如果需要刪除一些約束,則應該在屬性中存儲對每個約束的引用(例如,包含NSLayoutConstraint實例的NSArray屬性),然後使用NSLayoutConstraint或the PureLayout open-source library上的API停用/刪除這些約束。您應該只停用/刪除儘可能少的約束條件,因爲這樣做的計算量很大。另一方面,更改任何約束的constant是非常高效和鼓勵的,並且您不需要刪除或重新添加約束來執行此操作。

+0

非常感謝您的幫助。我在IB中添加了約束條件,將它們標記爲佔位符,並避免了異常。在考慮其餘的建議時,我認爲沒有多少理由不能在IB中指定所有這些約束條件,並完全刪除代碼。這是我第一個使用autolayout的項目,並且在視圖控制器上我需要在旋轉時添加/刪除約束,所以我基本上試圖在表視圖單元格上採用相同的方法。但正如你所指出的那樣,我不需要。再次感謝! –

+0

沒問題。如果你能從IB獲得這些限制,那就去做吧。但個人而言,我基本上完全停止使用IB,特別是當涉及到Auto Layout時。 IB看起來很簡單,但可以給任何東西造成很大的痛苦,除了最簡單的視圖設置(通常,你看到的是**而不是**你得到的!)。無論如何...隨意問你是否遇到其他絆腳石。 – smileyborg

+0

我不明白你的意思是什麼「不僅此複選框刪除了你添加的約束」你爲什麼要刪除剛添加的約束? – drc