代码之家  ›  专栏  ›  技术社区  ›  Viktor Kucera

StaveVIEW的轴变化给布局误差

  •  1
  • Viktor Kucera  · 技术社区  · 8 年前

    我有一个 UIStackView 根据轴的宽度改变轴。包括两个 UView 只有S。有一个非常简单的设置(可以复制/粘贴到默认的xcode项目):

    class ViewController: UIViewController {
    
        enum DisplayMode {
            case regular
            case compact
        }
    
        private let stackView = UIStackView(frame: .zero)
        private let firstView = UIView(frame: .zero)
        private let secondView = UIView(frame: .zero)
        private var firstViewWidth: NSLayoutConstraint?
        private var secondViewWidth: NSLayoutConstraint?
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            stackView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(stackView)
            NSLayoutConstraint.activate([
                stackView.topAnchor.constraint(equalTo: view.topAnchor),
                stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                ])
    
            firstView.backgroundColor = .red
            stackView.addArrangedSubview(firstView)
            firstView.heightAnchor.constraint(equalToConstant: 80).isActive = true
            firstViewWidth = firstView.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 1/3)
            firstViewWidth?.isActive = true
    
            secondView.backgroundColor = .black
            stackView.addArrangedSubview(secondView)
            secondView.heightAnchor.constraint(equalToConstant: 80).isActive = true
            secondViewWidth = secondView.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 2/3)
            secondViewWidth?.isActive = true
        }
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            if view.bounds.width < 400 {
                switchMode(.compact)
            } else {
                switchMode(.regular)
            }
        }
    }
    
    private extension ViewController {
    
        func switchMode(_ mode: DisplayMode) {
            switch mode {
            case .regular:
                stackView.axis = .horizontal
                firstViewWidth?.isActive = true
                secondViewWidth?.isActive = true
            case .compact:
                stackView.axis = .vertical
                firstViewWidth?.isActive = false
                secondViewWidth?.isActive = false
            }
        }
    }
    

    它工作得很好,但它给了我无意义的布局错误(输出中的所有约束似乎都很好),同时从紧凑型更改为常规型:

    (
        "<NSLayoutConstraint:0x6000002c1c20 UIView:0x7fbd4dc149a0.width == 0.333333*UIStackView:0x7fbd4df02430.width   (active)>",
        "<NSLayoutConstraint:0x6000002c2170 UIView:0x7fbd4dc14d90.width == 0.666667*UIStackView:0x7fbd4df02430.width   (active)>",
        "<NSLayoutConstraint:0x6000002f2080 'UISV-canvas-connection' UIStackView:0x7fbd4df02430.leading == UIView:0x7fbd4dc149a0.leading   (active)>",
        "<NSLayoutConstraint:0x6000002f1c70 'UISV-canvas-connection' H:[UIView:0x7fbd4dc14d90]-(0)-|   (active, names: '|':UIStackView:0x7fbd4df02430 )>",
        "<NSLayoutConstraint:0x6000002f05f0 'UISV-spacing' H:[UIView:0x7fbd4dc149a0]-(0)-[UIView:0x7fbd4dc14d90]   (active)>"
    )
    

    有人能解释为什么会这样吗动态轴是罪魁祸首吗?

    1 回复  |  直到 8 年前
        1
  •  3
  •   Bhavesh.iosDev    8 年前

    首先,viewDidLayoutSubviews不是处理oriantion更改的好选项。苹果推荐的方法是viewWillTransitionOnSize。 有关详细信息: What is the "right" way to handle orientation changes in iOS 8?

    第一次运行代码时,堆栈视图显示为垂直分布,第一个视图和第二个视图都有以下约束

    第一视图->

             Leading =  StackView Leading,
             Trailing =  StackView Trailing,
             Top      = StackView Top,
             height   =   80
    

    第二视图->

                Leading =  StackView Leading    ,
                  Top      = FirstView Top,
                  Trailing =  StackView Trailing,
                  height   =   80
    

    当您将设备从纵向旋转到横向时,堆栈视图以水平分布显示,并使以下约束处于活动状态。

    FirstView.Width=StackView.Width*1/3

    生成的firstview与上一个约束(stackview leading,stackview trailing)冲突。因为现在有两个宽度约束

    1. (firstview.leading=stackview leading&firstview.trailing=stackview trailing)=(firstview width==stackview.width)
    2. FirstView.Width=StackView.Width*1/3

    为什么stackview不自动删除冲突约束之一?

    -->因为这两个约束都具有高优先级(1000)。如果我们将firstviewwidth更改为较低的(999),并删除secondviewwidth约束,则解决方案将起作用

    尝试用以下代码替换您的代码

       import UIKit
    
    class ViewController: UIViewController {
    
        enum DisplayMode {
            case regular
            case compact
        }
    
        private let stackView = UIStackView(frame: .zero)
        private let firstView = UIView(frame: .zero)
        private let secondView = UIView(frame: .zero)
        private var firstViewWidth: NSLayoutConstraint?
        private var secondViewWidth: NSLayoutConstraint?
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            stackView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(stackView)
            NSLayoutConstraint.activate([
                stackView.topAnchor.constraint(equalTo: view.topAnchor),
                stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                ])
    
            firstView.backgroundColor = .red
            stackView.addArrangedSubview(firstView)
            firstView.heightAnchor.constraint(equalToConstant: 80).isActive = true
            firstViewWidth = firstView.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 1/3)
            firstViewWidth?.priority = UILayoutPriority(rawValue: 999)
            firstViewWidth?.isActive = true
    
            secondView.backgroundColor = .black
            stackView.addArrangedSubview(secondView)
            secondView.heightAnchor.constraint(equalToConstant: 80).isActive = true
    
    
            if self.view.bounds.width < 400 {
                self.switchMode(.compact)
            } else {
                self.switchMode(.regular)
            }
        }
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
    
        }
    
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            super.viewWillTransition(to: size, with: coordinator)
    
    
            coordinator.animate(alongsideTransition: { (context) in
    
            }) { (context) in
                if self.view.bounds.width < 400 {
                    self.switchMode(.compact)
                } else {
                    self.switchMode(.regular)
                }
            }
    
        }
    
    }
    
    private extension ViewController {
    
        func switchMode(_ mode: DisplayMode) {
            switch mode {
            case .regular:
                stackView.axis = .horizontal
                firstViewWidth?.isActive = true
    
            case .compact:
                stackView.axis = .vertical
                firstViewWidth?.isActive = false
            }
        }
    }