51

我正在尝试复制我目前在 Android 应用程序中的布局,但我不知道如何在 iOS 中进行,尤其是因为 iPhone 5 的高度。

我知道如何用“Android”术语来解释这一点,但过去几天我一直在尝试在 iOS 中做到这一点,但我无法让它发挥作用。

最好的解释方式:

  1. 我想要两种布局。顶部布局必须占 40%,底部必须占 60%。
  2. 在顶部布局中,它们必须是三个按钮,填满所有可能的空间(基本上占空间的 1/3)
  3. 在底部布局中,我想要一个 imageView,然后是一个 textView。

这是一个油漆模型。这可以在iOS中做到吗?我觉得布局比 android 更难创建。

在此处输入图像描述

4

6 回答 6

74

使用 Xcode 6.0,您现在可以在 Interface Builder 中指定比例宽度或高度。从superview获取百分比高度的步骤:

当子视图和它的父视图都被选中时,添加一个“等高”约束(或“等宽”,如果你希望有一个成比例的宽度)。

在此处输入图像描述

然后将您刚刚添加的约束的“乘数”更改为您需要的比例。例如,对于 50%,将其更改为 2。

如果您想将内部视图指定为超级视图的百分比,您可以反转约束中的项目:

在此处输入图像描述

现在您可以使用 0.5 的乘数(或您需要的任何其他比例):

在此处输入图像描述

在您的特定情况下,您可以在 2 个子视图之间定义“等高”约束,并将乘数更改为 1.5(底部是顶部大小的 1.5)或 0.6667(如果项目颠倒)。

另请参阅如何在情节提要中进行恒定的自动调整大小?

于 2014-10-12T09:36:25.763 回答
37

与其他答案相反,我认为您至少应该考虑自动布局系统。创建它是为了更容易构建可预测的布局,就像您描述的那样。自动布局由您对布局中的视图施加的约束决定。您可以直观地或以编程方式创建这些约束。在视觉上,您的第一个视图看起来像这样:

视觉约束

您看到的蓝线有许多约束,它们指定了按钮间距、按钮周围的空间以及按钮的高度和宽度。您可以看到几个约束条件=——我选择了所有三个按钮并给它们一个“等高”约束条件。

有一个很好的可视化格式语法,可以让您将约束描述为字符串。花一点时间看一下链接的文档——它不会花费太多时间来学习足够的内容,您可以阅读这些字符串。例如,您的顶部布局可以这样指定:

V:[button1]-[button2(==button1)]-[button3(==button1)]

括号==button1告诉布局系统使高度button2与. 表示按钮之间应使用标准间距;如果需要,您可以指定不同的间距。对于 10 点间距,请执行以下操作:button3button1-

V:|-10-[button1]-10-[button2(==button1)]-10-[button3(==button1)]-10-|

一旦有了这样的字符串,就可以使用以下方法将其转换为约束:+[NSLayoutConstraint constraintsWithVisualFormat:options:metrics:views:]

某些约束既不能直观地指定,也不能使用上述字符串指定。其中最主要的是您想要在两个视图之间设置恒定(但不相等)关系的那些,就像您的顶部和底部布局一样。您希望其中一个占用 40% 的可用垂直空间,而另一个占用 60%。使用以下方法执行此操作:+[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]。例如:

NSLayoutConstraint *c = [NSLayoutConstraint constraintWithItem:bottomView
                                                     attribute:NSLayoutAttributeHeight
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:topView
                                                    multiplier:1.5
                                                      constant:0];

这给了你一个约束,将高度设置为高度的bottomView1.5 倍topView(这是你想要的,因为 60/40 = 1.5)。

如果您以编程方式创建约束,则可以在创建(或加载)视图层次结构时将它们添加到适当的视图中。您的视图控制器的-viewDidLoad方法是这样做的好地方。

于 2013-08-22T04:47:31.490 回答
2

我不知道使用自动布局,因为我不使用它,但是在没有它的代码中,您可以创建两个UIViews并将它们的框架设置为:

CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height * 0.4f);
CGRectMake(0, self.view.frame.size.height * 0.4f, self.view.frame.size.width, self.view.frame.size.width * 0.6f);

然后在顶视图中,您可以添加带有框架的按钮(假设视图被调用view1):

CGRectmake(0, 0, self.view.frame.size.width, view1.frame.size.height * (1.0f/3.0f));
CGRectmake(0, view1.frame.size.height * (1.0f/3.0f), self.view.frame.size.width, view1.frame.size.height * (1.0f/3.0f));
CGRectmake(0, view1.frame.size.height * (2.0f/3.0f), self.view.frame.size.width, view1.frame.size.height * (1.0f/3.0f));
于 2013-08-11T18:06:23.377 回答
2

您需要添加一些约束才能使其正常工作。因此,这里是您需要的简要说明:

  1. 您将需要水平间距限制。一个如果用于顶视图,因为它与顶部的距离为零。另一个用于底部视图,因为它到底部的距离为零。两个视图之间还有一个约束,因为它们之间的间距也为零。

  2. 相同的原理将应用于顶视图内的按钮。将具有正确大小的按钮放在顶视图中并添加类似的约束,但现在您拥有三个元素(而不是两个)。因此,对顶部、底部和按钮之间的零间距的约束。

当您向视图添加组件时,它会为您创建一些约束。在顶部添加一个,在底部添加另一个,它将创建到顶部的间距和彼此之间的间距。要编辑它们,只需转到检查器,第五个选项卡(标尺),您将看到约束列表。最后,您可能想尝试约束菜单(我不知道是否有一个众所周知的名称)。它位于 Interface Builder 画布的右下角。看图片:

在此处输入图像描述

如果您需要进一步的帮助,请告诉我。

于 2013-08-22T01:42:42.460 回答
0

这可以在使用 Storyboard 时自动完成(您可能需要在这里和那里更改一两个设置)。创建 GUI 时,您可以在屏幕尺寸(3.5 和 4 英寸)之间切换,以确保两者看起来都不错。请参阅此问题的答案

您还可以查看本教程。这应该让您了解如何使用 GUI 布局。

于 2013-08-11T18:10:15.223 回答
0

请参阅GK100 的答案,但让我们用下面的 Q/A(s) 来扩展它。

如何在情节提要中进行恒定的自动调整大小?

尚不支持基于百分比的常量,人们尝试不同的解决方法,例如:

  • 创建不可见UIView的(s)作为具有宽度/高度的间隔,并指定一个乘数对Safe Area大小。
  • 人们甚至在代码中手动覆盖 Storyboard 约束:
    NSLayoutConstraint(item: myView, attribute: .top,
        relatedBy: .equal, toItem: myOtherView, attribute: .bottom,
        multiplier: 1,
        constant: UIScreen.main.bounds.width * 0.05)
    

虽然上述两个都令人头疼,但我的解决方案的缺点是您至少需要硬编码shortDimensionInDesignTime到您首选Interface Builder的预览大小(但比我的PercentConstraint班级好得多)。

用法:

  1. 将以下ResponsiveConstraint类的代码添加到您的项目中。
  2. Storyboard在编辑器中选择您的约束。
  3. Identity Inspector中,ResponsiveConstraint用作类。
import Foundation

/// Auto-Resize based Layout-Constraint.
/// Later at run-time, resizes initial constant to screen's short dimension.
///
/// For Mac's Catalyst. remember to set size-restrictions to something like:
/// ```
/// let portrait: CGSize = ResponsiveConstraint.maxSize;
/// scene.sizeRestrictions?.maximumSize = portrait;
/// scene.sizeRestrictions?.minimumSize = portrait;
/// ```
///
/// WARNING: supports only Storyboard (by use as class in Identity Inspector),
/// but there is no harm in using static provided field and methods.
///
public class ResponsiveConstraint: NSLayoutConstraint {
    var actualConstant: CGFloat = 0;
    public var isValid: Bool { actualConstant > 0 }

    /// Preset shorter dimension of screen, within Design-Time only
    /// (i.e. it's your Interface-Builder's preview size).
    public static let shortDimensionInDesignTime: CGFloat = 414;
    public static let longDimensionInDesignTime: CGFloat = 896;

    /// Normally same as "UIScreen.main.bounds.size" variable (i.e. screen size).
    /// But for Computers, we limit to a portrait-size (to look same as Mobile).
    public static var maxSize: CGSize {
        #if targetEnvironment(macCatalyst)
        return .init(
            width: Self.shortDimensionInDesignTime,
            height: Self.longDimensionInDesignTime);
        #else
        return UIScreen.main.bounds.size;
        #endif
    }

    public static var shortDimension: CGFloat {
        let screenSize: CGSize = Self.maxSize;
        return screenSize.width < screenSize.height
            ? screenSize.width : screenSize.height;
    }

    public static var longDimension: CGFloat {
        let screenSize: CGSize = Self.maxSize;
        return screenSize.height > screenSize.width
              ? screenSize.height : screenSize.width;
    }

    /// Does same as "init()" would, but for Storyboard.
    override public func awakeFromNib() {
        super.awakeFromNib()
        // Backup real constant (and continue if it's valid).
        self.actualConstant = self.constant
        guard isValid else { return }
        // Listens to screen rotate.
        NotificationCenter.default.addObserver(self,
            selector: #selector(onLayoutChange),
            name: UIDevice.orientationDidChangeNotification,
            object: nil)
        // Apply for current screen size.
        self.onLayoutChange();
    }

    deinit {
        guard isValid else { return }
        NotificationCenter.default.removeObserver(self)
    }

    /// Recalculates constant.
    @objc public func onLayoutChange() {
        guard isValid else { return }
        self.constant = Self.resize(self.actualConstant)
    }

    /// Converts constant from DesignTime to fit current max view-size.
    public static func resize(_ constant: CGFloat) -> CGFloat {
        return constant / Self.shortDimensionInDesignTime * Self.shortDimension;
    }
}

如何在情节提要中实现基于百分比的常量?

以下解决方案的缺点是需要在看到结果之前运行 App(即使在未来的 Xcode 版本中也可能不需要)。

用法:

  1. 将以下PercentConstraint类的代码添加到您的项目中。
  2. Storyboard在编辑器中选择您的约束。
  3. Identity Inspector中,PercentConstraint 用作类。
  4. Attribute Inspector,设置Percent of Screen字段。
  5. (可选)运行应用程序以查看结果。
import Foundation

/// Percent based Layout-Constraint.
/// Later at run-time, replaces constant with percentage of screen.
///
/// WARNING: supports only Storyboard (used as class in Identity Inspector).
///
class PercentConstraint: NSLayoutConstraint {

    @IBInspectable public var percentOfScreen: CGFloat = 0

    var isValid: Bool { percentOfScreen > 0 }
    var screenSize: CGSize { UIScreen.main.bounds.size }

    override func awakeFromNib() {
        super.awakeFromNib()

        guard isValid else { return }
        NotificationCenter.default.addObserver(self,
            selector: #selector(onLayoutChange),
            name: UIDevice.orientationDidChangeNotification,
            object: nil)
        self.onLayoutChange();
    }

    deinit {
        guard isValid else { return }
        NotificationCenter.default.removeObserver(self)
    }

    /// Recalculates constant.
    @objc func onLayoutChange() {
        guard isValid else { return }

        switch self.firstAttribute {
        case .height, .top, .topMargin, .bottom, .bottomMargin,
             .centerY, .centerYWithinMargins,
             .firstBaseline, .lastBaseline:
            self.constant = screenSize.height * percentOfScreen / 100.0;
        case .width, .leading, .leadingMargin, .trailing, .trailingMargin,
             .left, .leftMargin, .right, .rightMargin,
             .centerX, .centerXWithinMargins:
            self.constant = screenSize.width * percentOfScreen / 100.0;
        default:
            break;
        }
    }
}

请注意constant在运行时会覆盖它,但您可能仍想设置它(以了解它在运行之前的样子)。

于 2021-09-06T22:03:36.390 回答