UIStackView:内部子视图的破坏约束

Posted

技术标签:

【中文标题】UIStackView:内部子视图的破坏约束【英文标题】:UIStackView : broken constraints for internal subviews 【发布时间】:2022-01-20 21:56:57 【问题描述】:

我在 UIScrollView 中有一个 UIStackView 和一个简单的视图,如下所示:

class OnboardingView : UIView 
    private let onboardingImageView : UIImageView = 
        let view = UIImageView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.contentMode = .scaleAspectFit
        view.layer.cornerRadius = 8
        view.image = UIImage(named: "OnboardingImage")
        return view
    ()

    private lazy var scrollView: UIScrollView = 
        let view = UIScrollView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.showsVerticalScrollIndicator = false
        view.showsHorizontalScrollIndicator = false
        return view
    ()
       
    private lazy var scrollViewContainer: UIStackView = 
        let view = UIStackView()
        view.axis = .vertical
        view.spacing = 0
        view.distribution = .fillProportionally
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    ()
    
    private let imageArea : UIView = 
       let view = UIView()
       view.translatesAutoresizingMaskIntoConstraints = false
       return view
    ()
    
    private func setupScrollView()
    
        let constraints = [
            scrollView.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor),

            scrollViewContainer.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            scrollViewContainer.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            scrollViewContainer.topAnchor.constraint(equalTo: scrollView.topAnchor),
            scrollViewContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            scrollViewContainer.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
        ]
        
        NSLayoutConstraint.activate(constraints)
    
    
    private func setupAreas()
    
        let constraints = [
            imageArea.heightAnchor.constraint(equalToConstant: 400)
        ]
        
        NSLayoutConstraint.activate(constraints)
    
    
    private func setupImage()
    
        imageArea.addSubview(onboardingImageView)
        let constraints = [
            onboardingImageView.topAnchor.constraint(equalTo: imageArea.topAnchor, constant: 62),
            onboardingImageView.heightAnchor.constraint(equalToConstant: 334),
            onboardingImageView.leadingAnchor.constraint(equalTo: imageArea.leadingAnchor, constant: 35.5),
            onboardingImageView.trailingAnchor.constraint(equalTo: imageArea.trailingAnchor, constant : -35),
        ]
        
        NSLayoutConstraint.activate(constraints)
    
            
    private func setupLayout()
    
        self.addSubview(scrollView)
        scrollView.addSubview(scrollViewContainer)
        
        scrollViewContainer.addArrangedSubview(imageArea)
        
        setupScrollView()
        
        setupAreas()
        
        setupImage()
    
        
    private func setupViews()
    
        self.layer.backgroundColor = UIColor(red : 0.125, green: 0.306, blue: 0.78, alpha: 1).cgColor
                
        setupLayout()
    
    
    init(viewFrame : CGRect) 
        super.init(frame: viewFrame)
        setupViews()
    
     
    required init?(coder: NSCoder) 
         fatalError("init(coder:) has not been implemented for OnboardingView")
    

如果我为嵌套视图设置了一些约束,我会在控制台中收到约束错误提示:

(
    "<NSLayoutConstraint:0x600003efa440 H:|-(35.5)-[UIImageView:0x15110c090]   (active, names: '|':UIView:0x14fd086d0 )>",
    "<NSLayoutConstraint:0x600003efa490 UIImageView:0x15110c090.trailing == UIView:0x14fd086d0.trailing - 35   (active)>",
    "<NSLayoutConstraint:0x600003ed6030 'fittingSizeHTarget' UIView:0x14fd086d0.width == 0   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600003efa490 UIImageView:0x15110c090.trailing == UIView:0x14fd086d0.trailing - 35   (active)>
 

我无法解决。我不清楚当我将约束应用于 UIStackView 的排列视图(imageArea)项目时会出现什么问题,所以从我的角度来看应该没问题。我错过了什么?我应该如何解决它?

【问题讨论】:

约束设置中没有问题,constraints.last?.priority = .init(990) in setupImage(),将解决冲突。 【参考方案1】:

你的约束很好。

由于您的UIStackView 使用fillProportionally 作为其子视图,因此它从子视图中读取intrinsicContentSize

    // you can override and return non zero value to fix it
    class SubView: UIView 
        override var intrinsicContentSize: CGSize 
            return CGSize(width: 1, height: 1)
        
    

    private let imageArea : UIView = 
       let view = SubView()
       return view
    

    scrollViewContainer.addArrangedSubview(imageArea)

    // Set lower priority witch one is breaking (here 35 or -35 any one)
    private func setupImage() 
        imageArea.addSubview(onboardingImageView)
        let constraints = [
            onboardingImageView.topAnchor.constraint(equalTo: imageArea.topAnchor, constant: 62),
            onboardingImageView.heightAnchor.constraint(equalToConstant: 334),
            onboardingImageView.leadingAnchor.constraint(equalTo: imageArea.leadingAnchor, constant: 35.5),
            onboardingImageView.trailingAnchor.constraint(equalTo: imageArea.trailingAnchor, constant : -35),
        ]
        constraints.last?.priority = .init(990)
        NSLayoutConstraint.activate(constraints)
    

【讨论】:

以上是关于UIStackView:内部子视图的破坏约束的主要内容,如果未能解决你的问题,请参考以下文章

相对于堆栈视图高度约束 UIStackView 的子视图

以编程方式使用堆栈视图

如何在 UIStackView 内排列的子视图上设置自定义高度?

UIStackView使用介绍

UIStackView 和子视图大小类

水平 UIStackView 拉伸子视图