让 UIStackView 拥抱它的内容

Make UIStackView hug its content

我正在尝试使用主水平 UIStackView 和 2 个子垂直 UIStackViews 实现基本布局。

这是我想要的:

将左侧垂直堆栈的对齐方式设置为 fill 会扩展黄色标签,但会使红色和绿色视图具有相同的宽度。将对齐方式更改为 center 使左侧堆栈不拥抱它的子级(就像在图像中一样)。

知道应该怎么做吗?

参考我在下面上传的照片,我将 left vertical stack view 放在 UIView(紫色),然后将它们嵌入到带有 黄色标签 水平堆栈视图 中。我将分布设置为等间距间距为0,这样就没有间距了。

我将左侧垂直堆栈视图所有边的约束设置为0(这样左侧垂直堆栈视图就会拿整个紫色的 UIView)。也将 alignment 设置为 center 以便子视图水平居中。

左侧垂直堆栈视图中,我为尾随、前导和底部添加了一个约束0 以便左侧堆栈视图与最大的子视图(即绿色标签)一样“宽”。我将标签文本更改为居中,以便文本位于中心。

希望这能回答您的问题!

Check the image here

问题是 Alignment: Center 的垂直 UIStackView 没有固有宽度。

要获得所需的布局,您需要将 RedView 嵌入清晰的 UIView 或其他堆栈视图中,并将其对齐方式设置为居中。

这是堆栈视图的方法,因为我们需要的约束比“容器”视图少:

由于 RedView 有宽度和高度限制,它总是 50 x 50

“外部”堆栈视图 - HorizontalStack - 被限制在所有四个边上,带有 8 磅“填充”。它的属性是:

Axis: Horizontal
Alignment: Center
Distribution: Fill
Spacing: 0

LeftVerticalStack 属性是:

Axis: Vertical
Alignment: Fill
Distribution: Fill
Spacing: 0

RedContainerStack 属性是:

Axis: Vertical
Alignment: Center
Distribution: Fill
Spacing: 0

最后一个必要的设置 - 绿色标签需要拥抱其内容并抵抗压缩:

结果(标签中有几个不同的字符串):

这是 MyCollCell.xib 的来源:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
        <capability name="collection view cell content view" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="p9p-j5-QAK" customClass="MyCollCell" customModule="TableAdd" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="309" height="122"/>
            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
            <collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="7Um-4C-5Kc">
                <rect key="frame" x="0.0" y="0.0" width="309" height="122"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <stackView opaque="NO" contentMode="scaleToFill" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Ezy-2A-s1y" userLabel="HorizontalStack">
                        <rect key="frame" x="8" y="8" width="293" height="106"/>
                        <subviews>
                            <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="63E-WY-6Uf" userLabel="LeftVerticalStack">
                                <rect key="frame" x="0.0" y="18" width="92.5" height="70.5"/>
                                <subviews>
                                    <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="HgJ-l5-geV" userLabel="RedContainerStack">
                                        <rect key="frame" x="0.0" y="0.0" width="92.5" height="50"/>
                                        <subviews>
                                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="od9-kp-hKh" userLabel="RedView">
                                                <rect key="frame" x="21.5" y="0.0" width="50" height="50"/>
                                                <color key="backgroundColor" red="0.90437477830000002" green="0.1580897272" blue="0.20071530339999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <constraints>
                                                    <constraint firstAttribute="height" constant="50" id="R5P-yj-c9R"/>
                                                    <constraint firstAttribute="width" constant="50" id="zOV-Xo-aVe"/>
                                                </constraints>
                                            </view>
                                        </subviews>
                                    </stackView>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="251" horizontalCompressionResistancePriority="1000" text="Green Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xzN-ta-GJB">
                                        <rect key="frame" x="0.0" y="50" width="92.5" height="20.5"/>
                                        <color key="backgroundColor" red="0.1673075259" green="0.65853333469999997" blue="0.26840484139999998" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                        <nil key="textColor"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                </subviews>
                            </stackView>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Yellow Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ReT-76-3Fl">
                                <rect key="frame" x="92.5" y="43" width="200.5" height="20.5"/>
                                <color key="backgroundColor" red="0.99657744169999996" green="0.79237681630000001" blue="0.0001898294868" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                        </subviews>
                    </stackView>
                </subviews>
                <color key="backgroundColor" red="0.46202266219999999" green="0.83828371759999998" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                <constraints>
                    <constraint firstAttribute="trailing" secondItem="Ezy-2A-s1y" secondAttribute="trailing" constant="8" id="FlO-FQ-5GG"/>
                    <constraint firstItem="Ezy-2A-s1y" firstAttribute="top" secondItem="7Um-4C-5Kc" secondAttribute="top" constant="8" id="ct6-Mf-RVp"/>
                    <constraint firstAttribute="bottom" secondItem="Ezy-2A-s1y" secondAttribute="bottom" constant="8" id="dsT-ps-AIz"/>
                    <constraint firstItem="Ezy-2A-s1y" firstAttribute="leading" secondItem="7Um-4C-5Kc" secondAttribute="leading" constant="8" id="wAj-T5-Vzs"/>
                </constraints>
            </collectionViewCellContentView>
            <size key="customSize" width="309" height="122"/>
            <connections>
                <outlet property="greenLabel" destination="xzN-ta-GJB" id="1Tu-hu-JOb"/>
                <outlet property="yellowLabel" destination="ReT-76-3Fl" id="zTt-ML-CsA"/>
            </connections>
            <point key="canvasLocation" x="165.94202898550725" y="145.98214285714286"/>
        </collectionViewCell>
    </objects>
</document>

这是我用来生成上面显示的输出的示例代码:

import UIKit

private let reuseIdentifier = "Cell"

// cell sizing extension and systemLayoutSizeFitting implementation
// is not mine, but can be found here:
// https://www.robertpieta.com/autosizing-full-width-cells/
extension UICollectionView {
    var widestCellWidth: CGFloat {
        let insets = contentInset.left + contentInset.right
        return bounds.width - insets
    }
}

class MyCollCell: UICollectionViewCell {
    
    @IBOutlet var greenLabel: UILabel!
    @IBOutlet var yellowLabel: UILabel!

    override func systemLayoutSizeFitting(
        _ targetSize: CGSize,
        withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority,
        verticalFittingPriority: UILayoutPriority) -> CGSize {
        
        // Replace the height in the target size to
        // allow the cell to flexibly compute its height
        var targetSize = targetSize
        targetSize.height = CGFloat.greatestFiniteMagnitude
        
        // The .required horizontal fitting priority means
        // the desired cell width (targetSize.width) will be
        // preserved. However, the vertical fitting priority is
        // .fittingSizeLevel meaning the cell will find the
        // height that best fits the content
        let size = super.systemLayoutSizeFitting(
            targetSize,
            withHorizontalFittingPriority: .required,
            verticalFittingPriority: .fittingSizeLevel
        )
        
        return size
    }

}

class MyCollectionViewController: UICollectionViewController {

    let myData: [[String]] = [
        ["Green Label", "Yellow Label"],
        ["G", "Yellow Label"],
        ["Longer Green Label", "Yellow Label"],
        ["Green Label", "Yellow Label with too much text to fit here."],
        ["Green Label", "Yellow Label with .numberOfLines set to 0 will wrap when there's too much text."],
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Register cell classes
        self.collectionView.register(UINib(nibName: "MyCollCell", bundle: nil), forCellWithReuseIdentifier: reuseIdentifier)

        let layout = collectionView.collectionViewLayout
        if let flowLayout = layout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSize(
                width: collectionView.widestCellWidth,
                // Make the height a reasonable estimate to
                // ensure the scroll bar remains smooth
                height: 100
            )
        }

    }

    // MARK: UICollectionViewDataSource

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return myData.count
    }
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MyCollCell
    
        cell.greenLabel.text = myData[indexPath.row][0]
        cell.yellowLabel.text = myData[indexPath.row][1]
        
        cell.yellowLabel.numberOfLines = indexPath.item == 4 ? 0 : 1

        return cell
    }

}