Nested UIStackViews Broken Constraints

后端 未结 7 612
故里飘歌
故里飘歌 2021-01-31 02:59

I have a complex view hierarchy, built in Interface Builder, with nested UIStackViews. I get \"unsatisfiable constraints\" notices every time I hide some of my inner stackviews.

相关标签:
7条回答
  • 2021-01-31 03:30

    Another approach

    Try to avoid nested UIStackViews. I love them and build almost everything with them. But as I recognized that they secretly add constraints I try to only use them at the highest level and non-nested where possible. This way I can specify the 2nd highest priority .defaultHighto the spacing constraint which resolves my warnings.

    This priority is just enough to prevent most layout issues.

    Of course you need to specify some more constraints but this way you have full control of them and make your view layout explicit.

    0 讨论(0)
  • 2021-01-31 03:33

    Here's implementation of Senseful's suggestion #3 written as Swift 3 class using SnapKit constraints. I also tried overriding the properties, but never got it working without warnings, so I'll stick with wrapping UIStackView:

    class NestableStackView: UIView {
        private var actualStackView = UIStackView()
    
        override init(frame: CGRect) {
            super.init(frame: frame);
            addSubview(actualStackView);
            actualStackView.snp.makeConstraints { (make) in
                // Lower edges priority to allow hiding when spacing > 0
                make.edges.equalToSuperview().priority(999);
            }
        }
    
        convenience init() {
            self.init(frame: CGRect.zero);
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func addArrangedSubview(_ view: UIView) {
            actualStackView.addArrangedSubview(view);
        }
    
        func removeArrangedSubview(_ view: UIView) {
            actualStackView.removeArrangedSubview(view);
        }
    
        var axis: UILayoutConstraintAxis {
            get {
                return actualStackView.axis;
            }
            set {
                actualStackView.axis = newValue;
            }
        }
    
        open var distribution: UIStackViewDistribution {
            get {
                return actualStackView.distribution;
            }
            set {
                actualStackView.distribution = newValue;
            }
        }
    
        var alignment: UIStackViewAlignment {
            get {
                return actualStackView.alignment;
            }
            set {
                actualStackView.alignment = newValue;
            }
        }
    
        var spacing: CGFloat {
            get {
                return actualStackView.spacing;
            }
            set {
                actualStackView.spacing = newValue;
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-31 03:41

    This is a known problem with hiding nested stack views.

    There are essentially 3 solutions to this problem:

    1. Change the spacing to 0, but then you'll need to remember the previous spacing value.
    2. Call innerStackView.removeFromSuperview(), but then you'll need to remember where to insert the stack view.
    3. Wrap the stack view in a UIView with at least one 999 constraint. E.g. top@1000, leading@1000, trailing@1000, bottom@999.

    The 3rd option is the best in my opinion. For more information about this problem, why it happens, the different solutions, and how to implement solution 3, see my answer to a similar question.

    0 讨论(0)
  • 2021-01-31 03:42

    I hit a similar problem with UISV-hiding. For me, the solution was to reduce the priorities of my own constraints from Required (1000) to something less than that. When UISV-hiding constrains are added, they take priority and the constraints no longer clash.

    0 讨论(0)
  • 2021-01-31 03:42

    Ideally we could just set the priority of the UISV-spacing constraint to a lower value, but there doesn't appear to be any way to do that. :)

    I am having success setting the spacing property of the nested stack views to 0 before hiding, and restoring to the proper value after making it visible again.

    I think doing this recursively on nested stack views would work. You could store the original value of the spacing property in a dictionary and restore it later.

    My project only has a single level of nesting, so I am unsure if this would result in FPS problems. As long as you don't animate the changes in spacing, I don't think it would create too much of a hit.

    0 讨论(0)
  • 2021-01-31 03:51

    In my case I was adding width and height constraint to a navigation bar button, as per the advice above I only added lower priority to the constraints.

    open func customizeNavigationBarBackButton() {
            let _selector = #selector(UIViewController._backButtonPressed(_:))
            let backButtonView = UIButton(type: .custom)
            backButtonView.setImage(UIImage(named: "icon_back"), for: .normal)
            backButtonView.imageEdgeInsets = UIEdgeInsets.init(top: 0, left: -30, bottom: 0, right: 0)
            backButtonView.snp.makeConstraints { $0.width.height.equalTo(44).priority(900) }
            backButtonView.addTarget(self, action: _selector, for: .touchUpInside)
    
            let backButton = UIBarButtonItem(customView: backButtonView)
            self.navigationItem.leftBarButtonItem = backButton
        }
    
    0 讨论(0)
提交回复
热议问题