Ios XCode:如何在横向和纵向模式之间更改视图布局

Ios XCode:如何在横向和纵向模式之间更改视图布局,ios,swift,xcode,Ios,Swift,Xcode,在纵向模式下,我有四个视图,从视图控制器的顶部到底部,彼此重叠(见图) 然后,当设备转换到横向时,我想改变视图相对于彼此的位置(参见图2) 我希望视图4与视图2和3并排移动,它们都位于视图1下方 一些布局条件: 视图1在横向和纵向上都附着到视图控制器的顶部 在纵向模式下,视图4附着到视图控制器的左、右和下边距 视图2、3和4在纵向视图中水平居中 实现不同布局的最佳方法是什么 最优雅的解决方案是引用视图控制器代码中的约束,并在viewWillTransition中激活和停用它们吗?或者有没有

在纵向模式下,我有四个视图,从视图控制器的顶部到底部,彼此重叠(见图)

然后,当设备转换到横向时,我想改变视图相对于彼此的位置(参见图2)

我希望视图4与视图2和3并排移动,它们都位于视图1下方

一些布局条件:

  • 视图1在横向和纵向上都附着到视图控制器的顶部
  • 在纵向模式下,视图4附着到视图控制器的左、右和下边距
  • 视图2、3和4在纵向视图中水平居中
实现不同布局的最佳方法是什么


最优雅的解决方案是引用视图控制器代码中的约束,并在viewWillTransition中激活和停用它们吗?或者有没有一种方法可以使用vary for traits来实现这一点(我可以想象视图2、3和4水平居中会使这一点很难实现,并且在横向模式下为视图4添加新的约束)?

我使用约束数组,并根据方向激活/取消激活

var p = [NSLayoutConstraint]()
var l = [NSLayoutConstraint]()
var initialOrientation = true
var isInPortrait = false

override func viewDidLoad() {
    super.viewDidLoad()
    // add any subviews here
    view.turnOffAutoResizing()        
    // add constraints here....
    //    common constraints you can set their isActive = true
    //    otherwise, add in to portrait(p) and landscape(l) arrays
}

override func viewWillLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if initialOrientation {
        initialOrientation = false
        if view.frame.width > view.frame.height {
            isInPortrait = false
        } else {
            isInPortrait = true
        }
        view.setOrientation(p, l)
    } else {
        if view.orientationHasChanged(&isInPortrait) {
            view.setOrientation(p, l)
        }
    }
}
extension UIView {
    public func turnOffAutoResizing() {
        self.translatesAutoresizingMaskIntoConstraints = false
        for view in self.subviews as [UIView] {
           view.translatesAutoresizingMaskIntoConstraints = false
        }
    }
    public func orientationHasChanged(_ isInPortrait:inout Bool) -> Bool {
        if self.frame.width > self.frame.height {
            if isInPortrait {
                isInPortrait = false
                return true
            }
        } else {
            if !isInPortrait {
                isInPortrait = true
                return true
            }
        }
        return false
    }
    public func setOrientation(_ p:[NSLayoutConstraint], _ l:[NSLayoutConstraint]) {
        NSLayoutConstraint.deactivate(l)
        NSLayoutConstraint.deactivate(p)
        if self.bounds.width > self.bounds.height {
            NSLayoutConstraint.activate(l)
        } else {
            NSLayoutConstraint.activate(p)
        }
    }
}

我需要区分纵向和横向需要使用尺寸等级以外的东西,因为我的应用程序是通用的,iPad(除非使用拆分视图或滑出视图)总是正常尺寸。此外,您可以使用
viewWillTransistion(toSize:)
viewDidLoadSubviews()
而不是“viewWillLoadSubviews()”—但请始终进行测试,因为这些操作可能会在方向更改时执行多次

我们过去常常设置不同的约束集,并根据方向变化激活/停用它们。但如今,我们可以使用尺码等级和“不同的特征”

例如,我从一个简单的视图开始,选择一个紧凑的宽度大小类,然后选择“根据特征变化”:

然后,我添加适当的约束并单击“完成”:

然后,我选择一个“常规宽度大小类”,并重复该过程(“改变特征”,添加约束,单击“完成改变”:

然后,您将看到一个场景,该场景将具有一组完全不同的约束,用于紧凑宽度大小类和常规宽度大小类。例如,当我运行应用程序时,如果设备旋转,则会激活新的约束:

有关更多信息,请参阅WWDC 2016自适应布局视频:


“为特征而变化”是XCode 8的大小类。但是,如果您向某个大小类添加一个约束,它将被添加到所有约束中。您只能修改当前存在的约束,这在view 4需要一组新的约束时没有帮助。我认为使用viewWillTransition可能是首选方法,但我不确定是否有人需要有更好的方法来实现这一点感谢您提供了详细的答案,因此,是否可以在不影响其他尺寸类别的情况下,按不同的特征移动视图(例如,切换到横向,将红色和蓝色视图并排移动,然后添加约束)?是的,当你选择一个新的尺寸类时,将视图移动到你想要的地方,添加约束,你就完成了。我想你的问题中一定有我遗漏的东西:你一定有一些尺寸类的问题,我不理解。也许你可以解释一下尺寸类方法的问题……不要使用尺寸类s来确定设备方向。尺寸等级(“紧凑”和“常规”)和设备方向(“纵向”和“横向”)不相关。目前,所有iPad尺寸等级都是“常规”的,在设备旋转时不会改变。对于iPhone;在纵向模式下,一些型号的宽度是“紧凑”的,而一些型号的宽度是“常规”的如果你依赖于检测大小级别的变化,那么宽度和so在旋转时的行为会有所不同。当然,但通常也不应该使用设备方向来确定视图的布局方式。例如,仅仅因为iPad是横向的,并不意味着你拥有设备的全宽。你可能处于当设备处于横向模式时,plit view多任务处理,但您的应用程序仍有一小部分屏幕。大小类是一种有用但不完美的处理方法。有时您可以自己对布局进行更精确的检测。但在拆分视图多任务处理世界中,依赖设备方向通常是一种不好的方法实践。感谢您的回答,理想情况下,我希望通过故事板实现这一点,但如果不能通过故事板实现,这将是一个很好的选择!您可以通过故事板实现,但请记住IB只是一个“设计时”工具。这意味着什么(我面临这个问题)如果不是不可能的话,测试一个方向的改变是乏味的,尤其是对于iPad。