【问题标题】:How to use Auto Layout to stack views vertically in portrait mode and horizontally in landscape mode?如何使用自动布局在纵向模式下垂直堆叠视图并在横向模式下水平堆叠视图?
【发布时间】:2014-09-28 22:11:06
【问题描述】:

我有两个视图“视图 A”和“视图 B”。我想在横向模式下并排显示它们,但在纵向模式下一个接一个。如何使用自动布局来实现这一点?

【问题讨论】:

    标签: ios objective-c autolayout


    【解决方案1】:

    有了新的UIStackView(在 iOS 9.0 中引入),现在可以轻松实现。只需在情节提要中添加一个 UIStackView 与您的两个视图并定义所需的比例。

    然后在 ViewController 中,您可以直接在方向更改时调整轴的对齐方式:

    override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
        switch UIDevice.currentDevice().orientation {
        case .Portrait:
            self.stackview.axis = .Vertical
        case .PortraitUpsideDown:
            self.stackview.axis = .Vertical
        case .LandscapeLeft:
            self.stackview.axis = .Horizontal
        case .LandscapeRight:
            self.stackview.axis = .Horizontal
        default:
            self.stackview.axis = .Vertical
        }
    }
    

    【讨论】:

      【解决方案2】:

      写在 UIViewController 的 -viewDidLoad 方法中:

      [super viewDidLoad];
      
      // Assuming viewA and viewB have already been setup and have their 
      // translatesAutoresizingMaskIntoConstraints property set to NO
      
      NSDictionary *views = NSDictionaryOfVariableBindings(viewA,viewB);
      
      NSArray *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
      
      // Construct all of the constraints
      a1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewA]|" options:0 metrics:nil views:views];
      a2 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewB]|" options:0 metrics:nil views:views];
      a3 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewA][viewB]|" options:0 metrics:nil views:views];
      a4 = @[[NSLayoutConstraint constraintWithItem:viewB attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:viewA attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
      self.portraitConstraints = @[a1,a2,a3,a4];
      
      a5 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewA][viewB]|" options:0 metrics:nil views:views];
      a6 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewA]|" options:0 metrics:nil views:views];
      a7 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewB]|" options:0 metrics:nil views:views];
      a8 = @[[NSLayoutConstraint constraintWithItem:viewB attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:viewA attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
      
      self.landscapeConstraints = @[a5,a6,a7,a8];
      
      // Add all of the constraints
      for (NSArray *constraints in self.portraitConstraints) {
          [self.view addConstraints:constraints];
      }
      for (NSArray *constraints in self.landscapeConstraints) {
          [self.view addConstraints:constraints];
      }
      
      // Activate the constraints based on the orientation
      void(^constraintBlock)() = ^{
          // I'm using 'active' property instead of adding and removing constraints.  I haven't 
          // checked to see which way is "better" but it works well so I won't bother!
          if (UIDeviceOrientationIsPortrait([[UIApplication sharedApplication]statusBarOrientation])) {
              for (NSArray *constraints in self.landscapeConstraints) {
                  for (NSLayoutConstraint *c in constraints) {
                      c.active = NO;
                  }
              }
              for (NSArray *constraints in self.portraitConstraints) {
                  for (NSLayoutConstraint *c in constraints) {
                      c.active = YES;
                  }
              }
          } else {
              for (NSArray *constraints in self.portraitConstraints) {
                  for (NSLayoutConstraint *c in constraints) {
                      c.active = NO;
                  }
              }
              for (NSArray *constraints in self.landscapeConstraints) {
                  for (NSLayoutConstraint *c in constraints) {
                      c.active = YES;
                  }
              }
      
          }
      };
      
      // Call the block immediately to setup the constraints
      constraintBlock();
      
      // self.observer is a property I would then use in the viewController's -dealloc 
      // method to remove it as an observer of the default NSNotificationCenter
      self.observer = [[NSNotificationCenter defaultCenter]addObserverForName:UIApplicationDidChangeStatusBarOrientationNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
          // Update the constraints whenever the status bar orientation changes
          constraintBlock();
      }];
      

      还有很多其他方法可以做到这一点!

      【讨论】:

      • 我希望我可以在 IB 中使用尺寸等级来做到这一点。但这仍然有效。谢谢
      【解决方案3】:

      如果您的目标是 iOS 8,则可以利用新的 size classes。虽然这对以前版本的操作系统没有帮助,但它肯定会成为 iOS 8+ 的一项资产。

      您可以在 Interface Builder 中通过选择视图控制器并启用大小类来执行此操作:

      然后,在构建视图时,在 Storyboard 窗口底部设置要为其创建约束的尺寸类:

      您还可以直接从约束的属性检查器视图添加、修改和删除特定于大小类的约束:

      【讨论】:

        【解决方案4】:

        很遗憾,无法设置这样的布局来自动遵循您的要求。在 iOS8 中,借助 Size Classes(如上一个答案中已经提到的),您可以这样做,但仅限于 iPhone,它会在方向更改时更改类。由于您的示例具有 iPad 布局,因此没有这样的东西,您必须自己跟踪方向并相应地更改布局。

        编辑: 实际上,只要你只做 iPad 布局,或者你做通用布局,但 iPhone 和 iPad 有相同的布局,而且它只在方向变化时改变,就会有一个 hack 可以按需要运行: 呈现您嵌入在容器视图控制器中的视图控制器,并在容器中覆盖 -viewWillTransitionToSize:withTransitionCoordinator: 并通过调用 -setOverrideTraitCollection:forChildViewController: 来强制更改控制器的特征,其特征与容器的新大小相关(例如:size.width > size .height 在水平方向是UIUserInterfaceSizeClassRegular,在垂直方向是UIUserInterfaceSizeClassCompact

        这个过程的问题在于它改变了所有其他使用它的特性的类感知这个视图控制器的方式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-13
          • 1970-01-01
          • 1970-01-01
          • 2014-03-26
          • 2011-09-05
          相关资源
          最近更新 更多