让 UIStackView 拥抱它的内容
Make UIStackView hug its content
我正在尝试使用主水平 UIStackView
和 2 个子垂直 UIStackViews
实现基本布局。
这是我想要的:
- 红色视图有 2 个约束:width = height = 50
- 左侧垂直堆栈视图(带有绿色标签)应与其最大的子项一样宽,并且应将所有子项水平居中。
- 右边的垂直堆栈视图(带有黄色标签)应该占据所有可用空间 space。
将左侧垂直堆栈的对齐方式设置为 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
}
}
我正在尝试使用主水平 UIStackView
和 2 个子垂直 UIStackViews
实现基本布局。
这是我想要的:
- 红色视图有 2 个约束:width = height = 50
- 左侧垂直堆栈视图(带有绿色标签)应与其最大的子项一样宽,并且应将所有子项水平居中。
- 右边的垂直堆栈视图(带有黄色标签)应该占据所有可用空间 space。
将左侧垂直堆栈的对齐方式设置为 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
}
}