addArrangedSubview 是重叠视图

addArrangedSubview is overlapping views

我的问题 - 当我以编程方式将排列好的子视图添加到堆栈视图时,它们只是堆积在右上角,如下所示。他们为什么不堆积起来?我已经尝试了很多很多事情,包括使用具有固有大小的视图。

stackview 称为 mainStack,来自 xib。 mainStack 是一个垂直堆栈,Alignment 设置为 Fill,Distribution 设置为 Fill Equally(参见本问题底部的设置)。它包含一个带有蓝色背景的 UIView。为了这个问题,我使用 addArrangedSubviews 向 mainStack 添加了两个视图。这是我得到的:

这就是我所期望的:

这是 xib 的代码:

class TaskSheet: UIView {

    @IBOutlet var contentView: UIView!
    @IBOutlet weak var mainStack: UIStackView!


    override init(frame: CGRect) {
           super.init(frame: frame)
           setup()
       }

       required init?(coder aDecoder: NSCoder) {
           super.init(coder: aDecoder)
           setup()
       }

    func setup() {
        let nib = UINib(nibName: "TaskSheet", bundle: nil)
        nib.instantiate(withOwner: self, options: nil)
        contentView.frame = bounds
        addSubview(contentView)
    }
}

这就是我尝试向 mainStack 添加视图的方式:

class PDFSheet: UIView {

    var taskSheet: TaskSheet!
    var sheetArray = [UIView]()

    func makeSheet() -> [UIView] {
        taskSheet = TaskSheet(frame: CGRect(x: 0, y: 0, width: 612, height: 792))

        let newView1 = UIView(frame: CGRect(x: 0, y: 0, width: 240, height: 128))
        newView1.heightAnchor.constraint(equalToConstant: 128).isActive = true
        newView1.widthAnchor.constraint(equalToConstant: 240).isActive = true
        newView1.backgroundColor = .green

        let newView2 = UIView(frame: CGRect(x: 0, y: 0, width: 120, height: 64))
        newView2.heightAnchor.constraint(equalToConstant: 64).isActive = true
        newView2.widthAnchor.constraint(equalToConstant: 120).isActive = true
        newView2.backgroundColor = .yellow

        taskSheet.mainStack.addArrangedSubview(newView1)
        taskSheet.mainStack.addArrangedSubview(newView2)
        sheetArray.append(taskSheet)
        return sheetArray
    }
}

而且,这是显示堆栈视图设置的 xib,以防万一...

我想我明白你的意思了。

这是一个例子...

xib 布局(顶部标签、垂直堆栈视图、底部标签):

堆栈视图的属性:

TaskSheetPDFSheet 和示例视图控制器的代码:

class TaskSheet: UIView {

    @IBOutlet var contentView: UIView!
    @IBOutlet var mainStack: UIStackView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        let nib = UINib(nibName: "TaskSheet", bundle: nil)
        nib.instantiate(withOwner: self, options: nil)
        addSubview(contentView)
        NSLayoutConstraint.activate([

            // constrain contentView on all 4 sides with 8-pts "padding"
            contentView.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),

        ])
    }
}

class PDFSheet: UIView {

    var taskSheet: TaskSheet!
    var sheetArray = [UIView]()

    override init(frame: CGRect) {
        super.init(frame: frame)
        _ = makeSheet()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        _ = makeSheet()
    }

    func makeSheet() -> [UIView] {

        taskSheet = TaskSheet()

        let newView1 = UIView(frame: CGRect(x: 0, y: 0, width: 240, height: 128))
        newView1.heightAnchor.constraint(equalToConstant: 128).isActive = true
        newView1.widthAnchor.constraint(equalToConstant: 240).isActive = true
        newView1.backgroundColor = .green

        let newView2 = UIView(frame: CGRect(x: 0, y: 0, width: 120, height: 64))
        newView2.heightAnchor.constraint(equalToConstant: 64).isActive = true
        newView2.widthAnchor.constraint(equalToConstant: 120).isActive = true
        newView2.backgroundColor = .yellow

        taskSheet.mainStack.addArrangedSubview(newView1)
        taskSheet.mainStack.addArrangedSubview(newView2)
        sheetArray.append(taskSheet)

        addSubview(taskSheet)

        taskSheet.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([

            // constrain taskSheet on all 4 sides
            taskSheet.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
            taskSheet.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
            taskSheet.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
            taskSheet.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),

        ])

        return sheetArray
    }
}

class TaskViewController: UIViewController {

    var theSheetView: PDFSheet!

    override func viewDidLoad() {
        super.viewDidLoad()

        theSheetView = PDFSheet()
        theSheetView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(theSheetView)

        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            // constrain the sheet view on all top, leading, trailing with 32-pts "padding"
            theSheetView.topAnchor.constraint(equalTo: g.topAnchor, constant: 32.0),
            theSheetView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 32.0),
            theSheetView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -32.0),
            // NO height or bottom constraint
        ])

    }

}

这是 xib 文件的来源(以便于检查)编辑:糟糕,粘贴了错误的 xml 来源 -- 现已修复:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TaskSheet" customModule="scratchy" customModuleProvider="target">
            <connections>
                <outlet property="contentView" destination="TFh-sZ-4cx" id="zaP-M3-nAu"/>
                <outlet property="mainStack" destination="oGz-Bu-nCT" id="oCb-IB-Q4i"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB">
            <rect key="frame" x="0.0" y="0.0" width="375" height="315"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TFh-sZ-4cx">
                    <rect key="frame" x="8" y="8" width="359" height="299"/>
                    <subviews>
                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Top Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jZ3-yl-TaR">
                            <rect key="frame" x="0.0" y="0.0" width="359" height="21"/>
                            <color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
                            <nil key="textColor"/>
                            <nil key="highlightedColor"/>
                        </label>
                        <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="oGz-Bu-nCT">
                            <rect key="frame" x="0.0" y="21" width="359" height="257"/>
                        </stackView>
                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Bottom Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8bg-zV-k8Q">
                            <rect key="frame" x="0.0" y="278" width="359" height="21"/>
                            <color key="backgroundColor" red="0.92143100499999997" green="0.92145264149999995" blue="0.92144101860000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
                            <nil key="textColor"/>
                            <nil key="highlightedColor"/>
                        </label>
                    </subviews>
                    <color key="backgroundColor" red="0.36312681436538696" green="0.3205370306968689" blue="0.87124341726303101" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                    <constraints>
                        <constraint firstItem="oGz-Bu-nCT" firstAttribute="top" secondItem="jZ3-yl-TaR" secondAttribute="bottom" id="3FB-p9-cGU"/>
                        <constraint firstItem="8bg-zV-k8Q" firstAttribute="leading" secondItem="TFh-sZ-4cx" secondAttribute="leading" id="7bO-hv-chQ"/>
                        <constraint firstItem="oGz-Bu-nCT" firstAttribute="leading" secondItem="TFh-sZ-4cx" secondAttribute="leading" id="G5h-mz-ag5"/>
                        <constraint firstItem="jZ3-yl-TaR" firstAttribute="top" secondItem="TFh-sZ-4cx" secondAttribute="top" id="T1H-hj-4jJ"/>
                        <constraint firstAttribute="bottom" secondItem="8bg-zV-k8Q" secondAttribute="bottom" id="TYr-rY-NAc"/>
                        <constraint firstAttribute="trailing" secondItem="oGz-Bu-nCT" secondAttribute="trailing" id="VA9-gN-L1a"/>
                        <constraint firstAttribute="trailing" secondItem="8bg-zV-k8Q" secondAttribute="trailing" id="Vv5-P9-EGo"/>
                        <constraint firstItem="jZ3-yl-TaR" firstAttribute="leading" secondItem="TFh-sZ-4cx" secondAttribute="leading" id="XZc-QB-dm1"/>
                        <constraint firstItem="8bg-zV-k8Q" firstAttribute="top" secondItem="oGz-Bu-nCT" secondAttribute="bottom" id="ayn-E8-jo9"/>
                        <constraint firstAttribute="trailing" secondItem="jZ3-yl-TaR" secondAttribute="trailing" id="v4D-bJ-ltC"/>
                    </constraints>
                </view>
            </subviews>
            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
            <constraints>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="TFh-sZ-4cx" secondAttribute="trailing" constant="8" id="cgO-BT-ruo"/>
                <constraint firstItem="TFh-sZ-4cx" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="8" id="rAA-Vf-F0B"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="TFh-sZ-4cx" secondAttribute="bottom" constant="8" id="rhR-sH-KEq"/>
                <constraint firstItem="TFh-sZ-4cx" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="8" id="sag-F8-NuC"/>
            </constraints>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
            <point key="canvasLocation" x="148" y="49.925037481259373"/>
        </view>
    </objects>
</document>

结果:


EDIt 稍微 修改代码以生成 OP 添加的 "expected result" 图像:

  • 删除了标签(我把它们放在那里作为附加元素的示例)
  • 将所有 4 个边上的 stackView 限制为 0
  • 将 stackView 更改为 Aligment: FillDistribution: Fill Equally

TaskSheet.xib

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina4_7" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TaskSheet" customModule="scratchy" customModuleProvider="target">
            <connections>
                <outlet property="contentView" destination="TFh-sZ-4cx" id="zaP-M3-nAu"/>
                <outlet property="mainStack" destination="oGz-Bu-nCT" id="oCb-IB-Q4i"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB">
            <rect key="frame" x="0.0" y="0.0" width="375" height="315"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TFh-sZ-4cx">
                    <rect key="frame" x="8" y="8" width="359" height="299"/>
                    <subviews>
                        <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="oGz-Bu-nCT">
                            <rect key="frame" x="0.0" y="0.0" width="359" height="299"/>
                        </stackView>
                    </subviews>
                    <color key="backgroundColor" red="0.36312681436538696" green="0.3205370306968689" blue="0.87124341726303101" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                    <constraints>
                        <constraint firstItem="oGz-Bu-nCT" firstAttribute="leading" secondItem="TFh-sZ-4cx" secondAttribute="leading" id="G5h-mz-ag5"/>
                        <constraint firstAttribute="bottom" secondItem="oGz-Bu-nCT" secondAttribute="bottom" id="SIv-DX-ZpP"/>
                        <constraint firstAttribute="trailing" secondItem="oGz-Bu-nCT" secondAttribute="trailing" id="VA9-gN-L1a"/>
                        <constraint firstItem="oGz-Bu-nCT" firstAttribute="top" secondItem="TFh-sZ-4cx" secondAttribute="top" id="hPW-P3-dsk"/>
                    </constraints>
                </view>
            </subviews>
            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
            <constraints>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="TFh-sZ-4cx" secondAttribute="trailing" constant="8" id="cgO-BT-ruo"/>
                <constraint firstItem="TFh-sZ-4cx" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="8" id="rAA-Vf-F0B"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="TFh-sZ-4cx" secondAttribute="bottom" constant="8" id="rhR-sH-KEq"/>
                <constraint firstItem="TFh-sZ-4cx" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="8" id="sag-F8-NuC"/>
            </constraints>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
            <point key="canvasLocation" x="148" y="49.925037481259373"/>
        </view>
    </objects>
</document>

class TaskSheet: UIView {

    @IBOutlet var contentView: UIView!
    @IBOutlet var mainStack: UIStackView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        let nib = UINib(nibName: "TaskSheet", bundle: nil)
        nib.instantiate(withOwner: self, options: nil)
        addSubview(contentView)
        NSLayoutConstraint.activate([

            // constrain contentView on all 4 sides with 0-pts "padding"
            contentView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

        ])

    }
}

class PDFSheet: UIView {

    var taskSheet: TaskSheet!
    var sheetArray = [UIView]()

    override init(frame: CGRect) {
        super.init(frame: frame)
        _ = makeSheet()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        _ = makeSheet()
    }

    func makeSheet() -> [UIView] {

        taskSheet = TaskSheet()

        let newView1 = UIView()
        newView1.backgroundColor = .green

        let newView2 = UIView()
        newView2.backgroundColor = .yellow

        let spacerView = UIView()
        spacerView.backgroundColor = .clear

        // to get the "expected result" as shown in the OP's image,
        //  a 3-part stack view with equal heights,
        //  an easy way is to add a clear "spacer view" as the
        //  first - "top" - arranged subview

        taskSheet.mainStack.addArrangedSubview(spacerView)
        taskSheet.mainStack.addArrangedSubview(newView1)
        taskSheet.mainStack.addArrangedSubview(newView2)
        sheetArray.append(taskSheet)

        addSubview(taskSheet)

        taskSheet.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([

            // constrain taskSheet on all 4 sides
            taskSheet.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
            taskSheet.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
            taskSheet.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            taskSheet.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

        ])

        return sheetArray
    }
}

class TaskViewController: UIViewController {

    var theSheetView: PDFSheet!

    override func viewDidLoad() {
        super.viewDidLoad()

        theSheetView = PDFSheet()
        theSheetView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(theSheetView)

        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            // constrain the sheet view on top and leading at 40-pts (just so it's not flush with top/left of the view)
            // with specified width: 612 and height 792
            theSheetView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            theSheetView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            theSheetView.widthAnchor.constraint(equalToConstant: 612),
            theSheetView.heightAnchor.constraint(equalToConstant: 792),
        ])

    }

}

运行 在 iPad 第三代 Air 上的结果(以匹配 OP 的 width: 612, height: 792 规格):


另一个编辑:

这可能更接近 OP 的意图。

  • PDFSheet class 现在被视为 "view provider" 而不是视图本身。
  • 返回的 TaskSheet 视图数组的第一个元素(当前仅包含一个视图)将作为子视图添加到 viewController 的视图中。

与上面相同的 .xib 文件。

class TaskSheet: UIView {

    @IBOutlet var contentView: UIView!
    @IBOutlet var mainStack: UIStackView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    func setup() {
        let nib = UINib(nibName: "TaskSheet", bundle: nil)
        nib.instantiate(withOwner: self, options: nil)
        addSubview(contentView)

        NSLayoutConstraint.activate([

            // constrain contentView on all 4 sides with 0-pts "padding"
            contentView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

        ])

    }
}

class PDFSheet: UIView {

    var taskSheet: TaskSheet!
    var sheetArray = [UIView]()

    func makeSheet() -> [UIView] {

        taskSheet = TaskSheet(frame: CGRect(x: 0, y: 0, width: 612, height: 792))

        let newView1 = UIView()
        newView1.backgroundColor = .green

        let newView2 = UIView()
        newView2.backgroundColor = .yellow

        let spacerView = UIView()
        spacerView.backgroundColor = .clear

        // to get the "expected result" as shown in the OP's image,
        //  a 3-part stack view with equal heights,
        //  an easy way is to add a clear "spacer view" as the
        //  first - "top" - arranged subview

        taskSheet.mainStack.addArrangedSubview(spacerView)
        taskSheet.mainStack.addArrangedSubview(newView1)
        taskSheet.mainStack.addArrangedSubview(newView2)

        sheetArray.append(taskSheet)

        return sheetArray
    }
}

class TaskViewController: UIViewController {

    var theSheetView: PDFSheet!

    override func viewDidLoad() {
        super.viewDidLoad()

        theSheetView = PDFSheet()

        let views: [UIView] = theSheetView.makeSheet()

        guard let v = views.first else {
            fatalError("PDFSheet failed to create TaskSheet")
        }

    // note: At this point, the view has not been added to the
    // view hierarchy. If you're going to do something with it,
    // such as output it to a png or pdf, for example, you need
    // to tell auto-layout to do its work
    v.setNeedsLayout()
    v.layoutIfNeeded()

    let s = v.exportAsPdfFromView()
        view.addSubview(v)

    }

}

和(视觉上)相同的结果。这次,生成的 TaskSheet 视图只是添加到 viewController 的视图中,没有任何偏移: