【问题标题】:Make UIStackView hug its content让 UIStackView 拥抱它的内容
【发布时间】:2020-12-01 15:39:08
【问题描述】:

我正在尝试使用主要水平 UIStackView 实现基本布局,并带有 2 个子垂直 UIStackViews

这就是我想要的:

  • 红色视图有 2 个约束:宽度 = 高度 = 50
  • 左侧垂直堆栈视图(带有绿色标签)应与其最大的子级一样宽,并且应水平居中所有子级。
  • 右侧垂直堆栈视图(带有黄色标签)应占用所有可用空间。

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

有什么想法应该怎么做?

【问题讨论】:

    标签: ios swift autolayout uistackview


    【解决方案1】:

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

    要获得所需的布局,您需要将 RedView 嵌入到清晰的 UIView 或另一个堆栈视图中,并将其对齐设置为 Center。

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

    由于 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
        }
    
    }
    

    【讨论】:

    • 非常详细。它工作得非常好。谢谢! :)
    【解决方案2】:

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

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

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

    希望这能回答你的问题!

    Check the image here

    【讨论】:

    • 在您发布的图像中,greenLabel 比其固有内容大小更宽。我不想那样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-24
    • 1970-01-01
    • 2018-12-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-18
    • 2021-03-21
    相关资源
    最近更新 更多