ios 如何在横向和纵向模式之间更改视图布局?

esbemjvw  于 2023-05-19  发布在  iOS
关注(0)|答案(3)|浏览(133)

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

然后,我想在设备转换为横向时更改视图之间的相对位置(见图2)。

我希望视图4与视图2和视图3并排移动,并且它们都位于视图1下方。
一些布局条件:

  • 视图1以横向和纵向两种方式附接到视图控制器的顶部。
  • 视图4在纵向模式中被附加到视图控制器的左、右和底部边缘。
  • 视图2、3和4在纵向视图内水平居中。

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

shyt4zoc

shyt4zoc1#

我们过去常常设置不同的约束集,并根据方向变化激活/停用它们。但现在可以使用大小类和“因性状而异”。
例如,我从一个简单的视图开始,选择一个紧凑的宽度大小类,然后选择“Vary for traits”:

然后添加适当的约束并点击“Done varying”:

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

然后,您将得到一个场景,该场景将具有一组完全不同的约束,这些约束分别针对紧凑宽度大小类和常规宽度大小类处于活动状态。也就是说,当我运行应用程序时,如果设备旋转,则会激活新的约束:

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

thtygnil

thtygnil2#

我使用约束数组并根据方向激活/停用。

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()` -但总是要测试,因为这些可能会在方向改变时执行多次!

ktca8awb

ktca8awb3#

    • Xcode 13**

Xcode 13删除了“Vary for Traits”,取而代之的是为不同大小的类安装/卸载约束的能力。这里是@Rob的答案的更新,使用新的过程。
为了便于解释,我将使用“定向”这个术语,但它实际上是规模类。例如,iPhone上的纵向方向通常是常规高度/紧凑宽度。iPhone上的风景通常是紧凑的高度/规则的宽度。
首先,为一个方向设置所有约束。在这种情况下,红色和蓝色视图被约束到安全区域的边缘或彼此。我把蓝色的比例设定为1:1。

然后检查需要随方向变化的每个约束,并卸载其他方向(尺寸类)的约束。我将演示蓝色视图的前导对齐约束,它从纵向的安全区域的前导边缘变为横向的红色视图的后沿。
在蓝色视图的前导对齐约束上,选择“已安装”旁边的加号,然后选择“宽度:任意,高度:紧凑型(即景观)。

选择“添加变体”并取消选中新变体(“hC”旁边)。你基本上是告诉Xcode不要对紧凑的高度(横向)使用这个前导对齐约束。

现在,在界面生成器中更改方向。并在蓝色的前缘和红色的后缘之间添加约束。
注意:如果视图从屏幕上消失,您可以通过在左侧大纲中的视图之间拖动控件来添加新的约束。最后,所有视图都应该在两个方向上按预期显示。

选择新约束并为“宽度”添加新变量:任意,高度:常规(即肖像).

取消选中新的变量(“hR”旁边),告诉Xcode不要对常规高度(纵向)使用这个前导对齐约束。

如果约束不随方向改变,则两个方向将只有一个“已安装”约束。例如,蓝色视图的后缘和底边在两个方向上都被约束到安全区域的相应边缘。
另请注意,如果左侧轮廓中约束旁边的图标应用于“界面生成器”中的当前方向,则这些图标为蓝色;如果不应用于当前方向,则这些图标为灰色。

相关问题