2013-07-21 88 views

回答

39

編輯:使用Xcode 6中引入的Size Classes新概念,您可以輕鬆地爲Interface Builder中的特定大小類別設置不同的約束條件。大多數設備(例如所有當前的iPhone)在橫向模式下具有垂直尺寸類別的緊湊型

這是總體佈局的決定不是確定設備的方向一個更好的概念。

這就是說,如果你真的需要知道方向,UIDevice.currentDevice().orientation是要走的路。


原帖:

覆蓋的UIViewControllerupdateViewConstraints方法用於特定情況下提供佈局約束。這樣,佈局總是根據情況設置正確的方式。確保它們與故事板中創建的那些約束形成一套完整的約束。您可以使用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]; 

} 
+1

您不需要手動「觸發」約束更新。 'updateViewConstraints'方法已經做到了。這個評論也是由rokridi下面做出的,但是這個答案還沒有被編輯過。 –

+0

不要忘記,當您的視圖背景時,設備可能會旋轉。當應用程序被預先登陸時,你可以使用'setNeedsUpdateConstraints'。 –

+0

隨着iOS 8的發佈,* Size Classes *的概念將解決這個問題;) – knl

0

您可以將您的約束保存到屬性或變量,因爲縱向和橫向版本隨後在旋轉時設置並移除它們。

我已經完成了這個工作,使得我在xib中的初始視圖的約束,分配給視圖控制器中的插座。在旋轉時,我創建替代限制,刪除插座,但保留它們,插入替代品。

反轉回轉過程。

+0

如果設備在應用程序關閉時旋轉,會發生什麼情況?還有我應該注意的其他邊緣情況嗎? –

+1

當您的視圖不可見時,您的視圖將不會收到旋轉通知。我非常確定這一點。事實上,我記得在一些主題中看到這些,同時尋找一些相關的解決方案。我想你需要在viewWillAppear上檢查設備方向。 –

2

我下面和你同樣的方法(沒有筆尖文件或故事板)。您必須在updateViewConstraints方法中更新您的約束(通過檢查設備方向)。無需在updateViewConstraints中調用setNeedsUpdateConstraints,因爲只要更改設備方向,就會自動調用最後一個方法。

16

我使用的方法(好或壞)是在故事板編輯器中定義兩組約束(縱向和橫向)。

爲了避免地獄的故事板警告,我把所有的一組放在999的優先級,只是因爲它並沒有在任何地方顯示紅色。

然後,添加所有的約束,以出口集合:

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *portraitConstraints; 
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *landscapeConstraints; 

最後,我實現了我的ViewControllersviewWillLayout方法:

- (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); 
    } 
} 

這似乎是工作。我真的希望你可以在故事板編輯器中設置默認的活動屬性。

+0

非常好,謝謝!不幸的是,我也必須這樣做,因爲我支持iOS 7,所以無法充分利用Size類。我有一個自定義子視圖,做你在被覆蓋的「updateConstraints」 viewWillLayoutSubviews「這樣做,那麼我需要做的是從willAnimateRotationToInterfaceOrientation通話setNeedsUpdateConstraints。它確實工作正常。 – bandejapaisa

+0

我還想給你另外一個提醒,以便設置避免警告的優先級。 – bandejapaisa

+1

注意:活動屬性僅限iOS 8。另外,雖然注意到這一點,我也發現NSLayoutConstraint.activateConstraints()和NSLayoutConstraint.deactivateConstraints(),在其中您可以在IBOutletCollection通過一些便利的類方法。在iOS系統7,它幾乎相同的技術,但我使用self.removeConstraint和self.addConstraint,這在我的情況下工作,但我不知道如何可行的,這是適用於所有情況。 – bandejapaisa

-1

我的想法是通過改變約束優先級來處理方向。
假設優先級將是:

  • 景觀:910活性/ 10無活性的。
  • 肖像:920有效/ 20無效。

第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):

enter image description here

界面會自動rebuilded。

+0

人離開優先1000爲所有,只選中「安裝」。約束的優先級似乎允許聲明約束一次,使應用程序自動處理它們。但是你仍然使用它們來手動檢查每個旋轉動作。 –

+0

限制優先級不應該聲明一次,並且可以在輪換時更改。這種方法允許使用比其他代碼更少的代碼。 – Igor

相關問題