設備旋轉時是否可以更改約束條件?這可能如何實現?我可以使用自動佈局爲橫向和縱向方向提供不同的約束嗎?
一個簡單的例子可能是兩張圖片,縱向堆疊,另一張堆疊在一起,但橫向堆疊。
如果這是不可能的,我該怎樣才能完成這種佈局?
我正在構建我的視圖和代碼約束,而不是使用接口生成器。
設備旋轉時是否可以更改約束條件?這可能如何實現?我可以使用自動佈局爲橫向和縱向方向提供不同的約束嗎?
一個簡單的例子可能是兩張圖片,縱向堆疊,另一張堆疊在一起,但橫向堆疊。
如果這是不可能的,我該怎樣才能完成這種佈局?
我正在構建我的視圖和代碼約束,而不是使用接口生成器。
編輯:使用Xcode 6中引入的Size Classes新概念,您可以輕鬆地爲Interface Builder中的特定大小類別設置不同的約束條件。大多數設備(例如所有當前的iPhone)在橫向模式下具有垂直尺寸類別的緊湊型。
這是總體佈局的決定不是確定設備的方向一個更好的概念。
這就是說,如果你真的需要知道方向,UIDevice.currentDevice().orientation
是要走的路。
原帖:
覆蓋的UIViewController
的updateViewConstraints
方法用於特定情況下提供佈局約束。這樣,佈局總是根據情況設置正確的方式。確保它們與故事板中創建的那些約束形成一套完整的約束。您可以使用IB設置您的常規約束,並將需要更改的對象標記爲在運行時刪除。
我用下面的實現來呈現不同的約束集的每個方向:
-(void)updateViewConstraints {
[super updateViewConstraints];
// constraints for portrait orientation
// use a property to change a constraint's constant and/or create constraints programmatically, e.g.:
if (!self.layoutConstraintsPortrait) {
UIView *image1 = self.image1;
UIView *image2 = self.image2;
self.layoutConstraintsPortrait = [[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[image1]-[image2]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(image1, image2)] mutableCopy];
[self.layoutConstraintsPortrait addObject:[NSLayoutConstraint constraintWithItem:image1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem: image1.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
[self.layoutConstraintsPortrait addObject:[NSLayoutConstraint constraintWithItem:image2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:image2.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
}
// constraints for landscape orientation
// make sure they don't conflict with and complement the existing constraints
if (!self.layoutConstraintsLandscape) {
UIView *image1 = self.image1;
UIView *image2 = self.image2;
self.layoutConstraintsLandscape = [[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[image1]-[image2]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(image1, image2)] mutableCopy];
[self.layoutConstraintsLandscape addObject:[NSLayoutConstraint constraintWithItem:image1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:image1.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
[self.layoutConstraintsLandscape addObject:[NSLayoutConstraint constraintWithItem:image2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem: image2.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
}
BOOL isPortrait = UIInterfaceOrientationIsPortrait(self.interfaceOrientation);
[self.view removeConstraints:isPortrait ? self.layoutConstraintsLandscape : self.layoutConstraintsPortrait];
[self.view addConstraints:isPortrait ? self.layoutConstraintsPortrait : self.layoutConstraintsLandscape];
}
現在,所有你需要做的是觸發約束更新每當形勢的變化。覆蓋willAnimateRotationToInterfaceOrientation:duration:
動畫的方向變化約束更新:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self.view setNeedsUpdateConstraints];
}
您可以將您的約束保存到屬性或變量,因爲縱向和橫向版本隨後在旋轉時設置並移除它們。
我已經完成了這個工作,使得我在xib中的初始視圖的約束,分配給視圖控制器中的插座。在旋轉時,我創建替代限制,刪除插座,但保留它們,插入替代品。
反轉回轉過程。
如果設備在應用程序關閉時旋轉,會發生什麼情況?還有我應該注意的其他邊緣情況嗎? –
當您的視圖不可見時,您的視圖將不會收到旋轉通知。我非常確定這一點。事實上,我記得在一些主題中看到這些,同時尋找一些相關的解決方案。我想你需要在viewWillAppear上檢查設備方向。 –
我下面和你同樣的方法(沒有筆尖文件或故事板)。您必須在updateViewConstraints
方法中更新您的約束(通過檢查設備方向)。無需在updateViewConstraints
中調用setNeedsUpdateConstraints
,因爲只要更改設備方向,就會自動調用最後一個方法。
我使用的方法(好或壞)是在故事板編輯器中定義兩組約束(縱向和橫向)。
爲了避免地獄的故事板警告,我把所有的一組放在999的優先級,只是因爲它並沒有在任何地方顯示紅色。
然後,添加所有的約束,以出口集合:
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *portraitConstraints;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *landscapeConstraints;
最後,我實現了我的ViewControllers
的viewWillLayout
方法:
- (void) viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
for (NSLayoutConstraint *constraint in self.portraitConstraints) {
constraint.active = (UIApplication.sharedApplication.statusBarOrientation == UIDeviceOrientationPortrait);
}
for (NSLayoutConstraint *constraint in self.landscapeConstraints) {
constraint.active = (UIApplication.sharedApplication.statusBarOrientation != UIDeviceOrientationPortrait);
}
}
這似乎是工作。我真的希望你可以在故事板編輯器中設置默認的活動屬性。
非常好,謝謝!不幸的是,我也必須這樣做,因爲我支持iOS 7,所以無法充分利用Size類。我有一個自定義子視圖,做你在被覆蓋的「updateConstraints」 viewWillLayoutSubviews「這樣做,那麼我需要做的是從willAnimateRotationToInterfaceOrientation通話setNeedsUpdateConstraints。它確實工作正常。 – bandejapaisa
我還想給你另外一個提醒,以便設置避免警告的優先級。 – bandejapaisa
注意:活動屬性僅限iOS 8。另外,雖然注意到這一點,我也發現NSLayoutConstraint.activateConstraints()和NSLayoutConstraint.deactivateConstraints(),在其中您可以在IBOutletCollection通過一些便利的類方法。在iOS系統7,它幾乎相同的技術,但我使用self.removeConstraint和self.addConstraint,這在我的情況下工作,但我不知道如何可行的,這是適用於所有情況。 – bandejapaisa
我的想法是通過改變約束優先級來處理方向。
假設優先級將是:
第1步:在帶有約束的Storyboard中創建橫向(或縱向)設計。
步驟2:對於約束,必須僅對於橫向模式設定的優先級被激活至10
步驟3:添加約束肖像模式和它們的優先級設置爲920
在willAnimateRotationToInterfaceOrientation
附加代碼:
for (NSLayoutConstraint *constraint in myView.constraints) {
if (UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication.statusBarOrientation)) {
if (constraint.priority == 10) constraint.priority = 910;
if (constraint.priority == 920) constraint.priority = 20;
} else {
if (constraint.priority == 20) constraint.priority = 920;
if (constraint.priority == 910) constraint.priority = 10;
}
}
這種方法的優點 - 在Interface Builder中輕鬆調整。當我們需要切換到任何方位,我們選擇按優先級的所有限制,並同時改變他們(910-> 10,20-> 920):
界面會自動rebuilded。
人離開優先1000爲所有,只選中「安裝」。約束的優先級似乎允許聲明約束一次,使應用程序自動處理它們。但是你仍然使用它們來手動檢查每個旋轉動作。 –
限制優先級不應該聲明一次,並且可以在輪換時更改。這種方法允許使用比其他代碼更少的代碼。 – Igor
您不需要手動「觸發」約束更新。 'updateViewConstraints'方法已經做到了。這個評論也是由rokridi下面做出的,但是這個答案還沒有被編輯過。 –
不要忘記,當您的視圖背景時,設備可能會旋轉。當應用程序被預先登陸時,你可以使用'setNeedsUpdateConstraints'。 –
隨着iOS 8的發佈,* Size Classes *的概念將解決這個問題;) – knl