如何使用具有不同对齐方式的子视图制作堆栈视图?

How to make a stack view with subviews that has different alignments?

我的目标

我正在制作一个 UI,上面有一个图像,下面有四个垂直排列的按钮。图像的高度取决于它的宽度,但它不应该太高以至于按钮不够 space。图像定位后,按钮可以填满 space 的其余部分。在水平方向上,图像应尽量占据屏幕的宽度,但按钮的宽度应恒定为 300,并水平居中。我知道这不是本地化的好主意,但我不打算本地化此应用程序。

一张图值1000字:

我只使用堆栈溢出徽标来替换我将要使用的实际图像。我不是在制作与 Stack Overflow 相关的应用程序。

此外,当垂直尺寸 class 紧凑时,图像视图应位于按钮的左侧(按钮仍然垂直排列)。

我想我基本上想要一个堆栈视图,其子视图对齐方式不同——图像视图与“填充”对齐,按钮与“中心”对齐。但是,我不知道该怎么做,所以我尝试使用嵌套堆栈视图来解决这个问题。

重现方式:

外部堆栈视图 top/bottom/leading/trailing 全部固定到 VC 的视图,对齐设置为“填充”,分布设置为“按比例填充”。我选择“按比例填充”是因为我发现如果我使用足够大的图像效果最好。我在外部堆栈视图的轴上添加了一个变体,以便在垂直尺寸 class 紧凑时将其设置为水平。

外部堆栈视图有 2 个排列的子视图 - 图像视图和内部堆栈视图,其分布设置为“填充”,对齐方式设置为“居中”。

内部堆栈视图然后具有这些按钮。每个按钮都有一个恒定的宽度限制。

我认为应该可以完成这项工作。当我 运行 应用程序时,我看到一些“无法满足约束”警告:

(
    "<NSLayoutConstraint:0x6000038d2b20 UIButton:0x7f82d250e3c0'BUTTON1'.width == 300   (active)>",
    "<NSLayoutConstraint:0x6000038d98b0 'fittingSizeHTarget' UIStackView:0x7f82d250e230.width == 0   (active)>",
    "<NSLayoutConstraint:0x6000038d8cd0 'UISV-canvas-connection' UIStackView:0x7f82d250e230.leading == _UILayoutSpacer:0x6000024e01e0'UISV-alignment-spanner'.leading   (active)>",
    "<NSLayoutConstraint:0x6000038d9130 'UISV-canvas-connection' UIStackView:0x7f82d250e230.centerX == UIButton:0x7f82d250e3c0'BUTTON1'.centerX   (active)>",
    "<NSLayoutConstraint:0x6000038d8e10 'UISV-spanning-boundary' _UILayoutSpacer:0x6000024e01e0'UISV-alignment-spanner'.leading <= UIButton:0x7f82d250e3c0'BUTTON1'.leading   (active)>"
)
Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000038d2b20 UIButton:0x7f82d250e3c0'BUTTON1'.width == 300   (active)>

每个按钮都有一组!除此之外,布局似乎工作正常,但我不知道什么时候这实际上会破坏进一步的东西。

我查看了哪些约束是冲突的,哪些被破坏了。显然有一个 StackView.width = 0 约束与 button1.width = 300 约束冲突,后者被破坏了。我不知道 StackView.width = 0 约束来自哪里,也不知道它指的是哪个堆栈视图:(

如何防止约束被破坏?

如果我的重现步骤不够清楚,这里是 VC:

的故事板代码
    <scene sceneID="N06-uN-k0T">
        <objects>
            <viewController id="BdK-0G-Xl3" userLabel="Probelm VC" sceneMemberID="viewController">
                <view key="view" contentMode="scaleToFill" id="OpP-9z-h5Y">
                    <rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                    <subviews>
                        <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillProportionally" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="nvX-HI-4ox">
                            <rect key="frame" x="16" y="16" width="635" height="343"/>
                            <subviews>
                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="soLogo" translatesAutoresizingMaskIntoConstraints="NO" id="1RQ-jl-D6x">
                                    <rect key="frame" x="0.0" y="0.0" width="327" height="343"/>
                                </imageView>
                                <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="ROW-bu-Xw7">
                                    <rect key="frame" x="335" y="0.0" width="300" height="343"/>
                                    <subviews>
                                        <button opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hpX-JI-GT0">
                                            <rect key="frame" x="0.0" y="0.0" width="300" height="78.5"/>
                                            <color key="backgroundColor" red="0.23137254900000001" green="0.4823529412" blue="0.23137254900000001" alpha="1" colorSpace="calibratedRGB"/>
                                            <constraints>
                                                <constraint firstAttribute="width" constant="300" id="Ogw-Xr-6DE"/>
                                            </constraints>
                                            <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                            <state key="normal" title="BUTTON1"/>
                                        </button>
                                        <button opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dJT-f3-AJM">
                                            <rect key="frame" x="0.0" y="88.5" width="300" height="78"/>
                                            <color key="backgroundColor" red="0.23137254900000001" green="0.4823529412" blue="0.23137254900000001" alpha="1" colorSpace="calibratedRGB"/>
                                            <constraints>
                                                <constraint firstAttribute="width" constant="300" id="AII-kQ-4cH"/>
                                            </constraints>
                                            <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                            <state key="normal" title="BUTTON2"/>
                                        </button>
                                        <button opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NZx-LS-e5l">
                                            <rect key="frame" x="0.0" y="176.5" width="300" height="78.5"/>
                                            <color key="backgroundColor" red="0.23137254900000001" green="0.4823529412" blue="0.23137254900000001" alpha="1" colorSpace="calibratedRGB"/>
                                            <constraints>
                                                <constraint firstAttribute="width" constant="300" id="rDS-bU-7lE"/>
                                            </constraints>
                                            <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                            <state key="normal" title="BUTTON3"/>
                                        </button>
                                        <button opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="onC-ie-E6z">
                                            <rect key="frame" x="0.0" y="265" width="300" height="78"/>
                                            <color key="backgroundColor" red="0.23137254900000001" green="0.4823529412" blue="0.23137254900000001" alpha="1" colorSpace="calibratedRGB"/>
                                            <constraints>
                                                <constraint firstAttribute="width" constant="300" id="4K7-7z-YHF"/>
                                            </constraints>
                                            <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                            <state key="normal" title="BUTTON4"/>
                                        </button>
                                    </subviews>
                                </stackView>
                            </subviews>
                            <variation key="heightClass=compact" axis="horizontal"/>
                        </stackView>
                    </subviews>
                    <viewLayoutGuide key="safeArea" id="wgl-gV-tap"/>
                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    <constraints>
                        <constraint firstItem="nvX-HI-4ox" firstAttribute="top" secondItem="wgl-gV-tap" secondAttribute="top" constant="16" id="XjW-5g-rZt"/>
                        <constraint firstItem="nvX-HI-4ox" firstAttribute="leading" secondItem="wgl-gV-tap" secondAttribute="leading" constant="16" id="eYR-DM-EMX"/>
                        <constraint firstItem="wgl-gV-tap" firstAttribute="trailing" secondItem="nvX-HI-4ox" secondAttribute="trailing" constant="16" id="xhu-Xr-XWi"/>
                        <constraint firstItem="wgl-gV-tap" firstAttribute="bottom" secondItem="nvX-HI-4ox" secondAttribute="bottom" constant="16" id="z5a-h7-F7G"/>
                    </constraints>
                </view>
            </viewController>
            <placeholder placeholderIdentifier="IBFirstResponder" id="MTA-xX-zRK" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
        </objects>
        <point key="canvasLocation" x="-183" y="865"/>
    </scene>

好的 - 根据您发布的图片,我将为您的“徽标图片”使用 2:1 纵横比...

从“基本”布局开始:

限制条件如下:

Outer StackView 属性:

Buttons StackView 属性:

在这一点上,我们应该“准备好”使用“纵向”布局。

让我们添加一些特征变化...

要获得此布局:

我们将为 OuterStack 的 AxisAlignment 添加“Width: Any / Height: Compact”:

我们将为 ButtonsStack 的 Alignment 添加“宽度:任意/高度:紧凑”:

如果我们运行那个(iPhone 12 sim),我们得到这个:

我们接近了,但是...我们在调试控制台中收到了一大堆自动布局警告/错误消息。

那是因为(根据我的经验)自动布局需要进行多次“通过”才能全面评估布局,尤其是在将纵横比约束与堆栈视图混合使用时。

为了摆脱它,我们将给图像视图的纵横比约束一个低于要求的值 Priority:

这实际上允许自动布局在其多个布局过程中打破约束

这是完整的故事板源代码,因此您可以更仔细地检查事物:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="2ea-UR-he0">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="Kgd-qU-TMb">
            <objects>
                <viewController id="2ea-UR-he0" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="oLK-rW-5I7">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="bhh-Ra-9sa" userLabel="OuterStack">
                                <rect key="frame" x="16" y="16" width="343" height="635"/>
                                <subviews>
                                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="soLogo" translatesAutoresizingMaskIntoConstraints="NO" id="zgv-cZ-TeJ">
                                        <rect key="frame" x="0.0" y="0.0" width="343" height="171.5"/>
                                        <constraints>
                                            <constraint firstAttribute="width" secondItem="zgv-cZ-TeJ" secondAttribute="height" multiplier="2:1" priority="750" id="0U3-Il-GO7"/>
                                        </constraints>
                                    </imageView>
                                    <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="bq4-eZ-cNR" userLabel="ButtonsStack">
                                        <rect key="frame" x="0.0" y="171.5" width="343" height="463.5"/>
                                        <subviews>
                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xKR-aa-qSg">
                                                <rect key="frame" x="21.5" y="0.0" width="300" height="108.5"/>
                                                <color key="backgroundColor" red="0.16262620689999999" green="0.55341011289999997" blue="0.26840737460000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <constraints>
                                                    <constraint firstAttribute="width" constant="300" id="Omy-Kc-rR2"/>
                                                </constraints>
                                                <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                                <state key="normal" title="Button 1"/>
                                            </button>
                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KTN-zM-mhK">
                                                <rect key="frame" x="21.5" y="118.5" width="300" height="108.5"/>
                                                <color key="backgroundColor" red="0.16262620689999999" green="0.55341011289999997" blue="0.26840737460000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <constraints>
                                                    <constraint firstAttribute="width" constant="300" id="7cU-at-nc8"/>
                                                </constraints>
                                                <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                                <state key="normal" title="Button 2"/>
                                            </button>
                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jP5-6h-Pb5">
                                                <rect key="frame" x="21.5" y="237" width="300" height="108"/>
                                                <color key="backgroundColor" red="0.16262620689999999" green="0.55341011289999997" blue="0.26840737460000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <constraints>
                                                    <constraint firstAttribute="width" constant="300" id="9qP-Dd-5pW"/>
                                                </constraints>
                                                <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                                <state key="normal" title="Button 3"/>
                                            </button>
                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jhr-47-0CJ">
                                                <rect key="frame" x="21.5" y="355" width="300" height="108.5"/>
                                                <color key="backgroundColor" red="0.16262620689999999" green="0.55341011289999997" blue="0.26840737460000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <constraints>
                                                    <constraint firstAttribute="width" constant="300" id="c8c-CT-Sz5"/>
                                                </constraints>
                                                <fontDescription key="fontDescription" name="ChalkboardSE-Regular" family="Chalkboard SE" pointSize="52"/>
                                                <state key="normal" title="Button 4"/>
                                            </button>
                                        </subviews>
                                        <variation key="heightClass=compact" alignment="fill"/>
                                    </stackView>
                                </subviews>
                                <variation key="heightClass=compact" alignment="center" axis="horizontal"/>
                            </stackView>
                        </subviews>
                        <viewLayoutGuide key="safeArea" id="guT-8A-oyH"/>
                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
                        <constraints>
                            <constraint firstItem="bhh-Ra-9sa" firstAttribute="leading" secondItem="guT-8A-oyH" secondAttribute="leading" constant="16" id="6me-RD-7Uz"/>
                            <constraint firstItem="guT-8A-oyH" firstAttribute="trailing" secondItem="bhh-Ra-9sa" secondAttribute="trailing" constant="16" id="7oW-d7-Lhf"/>
                            <constraint firstItem="guT-8A-oyH" firstAttribute="bottom" secondItem="bhh-Ra-9sa" secondAttribute="bottom" constant="16" id="9Kz-Ok-e52"/>
                            <constraint firstItem="bhh-Ra-9sa" firstAttribute="top" secondItem="guT-8A-oyH" secondAttribute="top" constant="16" id="DY2-wl-5Y8"/>
                        </constraints>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="b0R-on-Vlz" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1055" y="206"/>
        </scene>
    </scenes>
    <resources>
        <image name="soLogo" width="230" height="115"/>
        <systemColor name="systemBackgroundColor">
            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
        </systemColor>
    </resources>
</document>