需要根据我的设备大小按比例调整 StackView 的大小

Need to size StackView proportionately to my device size

我正在用代码构建我的屏幕,其中包含一个包含我的文本字段的 stackView。这个 stackView 在 iPhone 8/8Plus/X 设备上看起来不错,但在 SE 屏幕上太大了。我意识到我需要根据设备大小按比例调整 stackView 的大小,但不确定如何以编程方式进行。我看过的所有教程都展示了如何在 IB 中执行此操作。

这是我正在使用的代码:

        let textFieldStackView = UIStackView(arrangedSubviews: [nameTextField, emailTextField, passwordTextField])
        textFieldStackView.translatesAutoresizingMaskIntoConstraints = false
        textFieldStackView.distribution = .fillEqually
        textFieldStackView.axis = .vertical
        textFieldStackView.spacing = 10

        view.addSubview(textFieldStackView)

        textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 100).isActive = true
        textFieldStackView.heightAnchor.constraint(equalToConstant: 150).isActive = true
        textFieldStackView.widthAnchor.constraint(equalToConstant: 350).isActive = true
        textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

不仅是宽度,我还需要相应地增加高度。我将如何在代码中执行此操作?

最简单的解决方案(但不是最漂亮的解决方案)是使用 UIScreen.main.frame 来计算约束常数。 UIScreen.main.frame 表示整个屏幕的框架,并且在每个设备上都不同(iPhone SE 上为 320x568,iPhone 8 上为 375x667)。

如果您希望堆栈视图在每台设备上看起来都一样,您的代码可能如下所示:

    let textFieldStackView = UIStackView(arrangedSubviews: [nameTextField, emailTextField, passwordTextField])
    textFieldStackView.translatesAutoresizingMaskIntoConstraints = false
    textFieldStackView.distribution = .fillEqually
    textFieldStackView.axis = .vertical
    textFieldStackView.spacing = UIScreen.main.frame.height / 66.7 // will be 10 on iPhone 8

    view.addSubview(textFieldStackView)

    textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: UIScreen.main.frame.height / 6.67).isActive = true
    textFieldStackView.heightAnchor.constraint(equalToConstant: UIScreen.main.frame.height / 4.45).isActive = true
    textFieldStackView.widthAnchor.constraint(equalToConstant: UIScreen.main.frame.width / 1.07).isActive = true
    textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

如果您需要将视图裁剪到屏幕边缘,您应该使用 safeArea 作为 top/bottom 约束,并使用前导和尾随边距作为前导和尾随约束。这样,您的视图在您需要的任何设备上都会看起来很棒。
例如:

yourView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
yourView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
yourView.leadingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.leadingAnchor).isActive = true
yourView.trailingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.trailingAnchor).isActive = true

是ios11码。要支持 iOS 10 布局用于控制器的顶部和底部锚点,请使用 self.topLayoutGuide.bottomAnchor 和 self.bottomLayoutGuide.topAnchor。
在自动布局中使用自动布局是非常正确的,而不是像顶部评论中那样使用 UISCreenSizes。 https://useyourloaf.com/blog/safe-area-layout-guide/ 学习本指南,帮助不大。

编辑:
如果你想让你的视图在中心并根据屏幕尺寸改变它的大小并且同时具有相等的间距前导和尾随我看到的最好的方法是在你的视图的左侧和右侧使用支持视图,它们将具有相同的宽度.调整中心(您的)视图的压缩阻力,您将使它变得更薄或更宽。
像这样:

编辑 2:
根据您的评论和代码:

yourView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
yourView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
yourView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
yourView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor, constant: 10).isActive = true
yourView.bottomAnchor.constraintGreaterThanOrEqualToSystemSpacingBelow(self.view.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0).isActive = true
yourView.topAnchor.constraintGreaterThanOrEqualToSystemSpacingBelow(self.view.safeAreaLayoutGuide.topAnchor, multiplier: 1.0).isActive = true
yourView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
yourView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)

我无法对 kiwisip 的答案发表评论,所以我必须写在这里 - 使用 UIScreen.main.frame 不是一个好的解决方案,最好依靠视图的大小,您可以在其中添加您的堆栈视图作为子视图。在您的示例中,您必须将 widthAnchor 约束替换为如下内容:

textFieldStackView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true

另一方面 - 使用 superview 大小除以常量而不是仅使用常量值可能是个好主意,但这取决于您的布局。

您可以将值从一个设备屏幕(下例中的 6S)缩放到另一个..

func ScaleWidth(CGFloat value) -> CGFloat {
    return round(value * (UIScreen.main.bounds.size.width / CGFloat(375.0)));
}

func ScaleHeight(CGFloat value) -> CGFloat {
    return round(value * (UIScreen.main.bounds.size.height / CGFloat(667.0)));
}



textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: ScaleHeight(100.0)).isActive = true
textFieldStackView.heightAnchor.constraint(equalToConstant: ScaleHeight(150.0)).isActive = true
textFieldStackView.widthAnchor.constraint(equalToConstant: ScaleWidth(350.0)).isActive = true
textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

另一种选择是使用约束乘数:

func AspectWidth() -> CGFloat {
    return round(UIScreen.main.bounds.size.width / CGFloat(375.0));
}

func AspectHeight() -> CGFloat {
    return round(UIScreen.main.bounds.size.height / CGFloat(667.0));
}



textFieldStackView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 100.0, multiplier: AspectHeight()).isActive = true
textFieldStackView.heightAnchor.constraint(equalToConstant: 150.0, multiplier: AspectHeight()).isActive = true
textFieldStackView.widthAnchor.constraint(equalToConstant: 350.0, multiplier: AspectWidth()).isActive = true
textFieldStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true