代码之家  ›  专栏  ›  技术社区  ›  modeller

当设备方向改变时,我应该禁用或删除布局约束吗?

  •  1
  • modeller  · 技术社区  · 7 年前

    我正在以编程方式创建自适应布局。我的计划是有两套约束: portraitConstraints landscapeConstraints . 当设备改变方向时( willTransition(to:) )禁用以前的规则,并启用新规则。

    portraitConstraints.append(photoView.topAnchor.constraint(equalTo: view.topAnchor))
    ....
    portraitConstraints.append(canvas.trailingAnchor.constraint(equalTo: photoView.trailingAnchor))
    
    
    landscapeConstraints.append(photoView.topAnchor.constraint(equalTo: view.topAnchor))
    ....
    landscapeConstraints.append(collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor))
    

    要处理设备旋转:

    // disable all constraints first
    for constraint in self.portraitConstraints {
      constraint.isActive = false
      print("before: portraitConstraints: \(constraint)")
    }
    
    for constraint in self.landscapeConstraints {
      constraint.isActive = false
      print("before: landscapeConstraint: \(constraint)")
    }
    
    // enable these after rotation
    if UIDevice.current.orientation.isLandscape {
      // gw: note: always disable previous rules first, then do enabling new rules
      // implications: if you enable new rule first, you will have a short time period with conflicting rules
    
      for constraint in self.landscapeConstraints {
        constraint.isActive = true
      }
    
      for constraint in self.portraitConstraints {
        print("after: portraitConstraints: \(constraint)")
      }
    
      for constraint in self.landscapeConstraints {
        print("after: landscapeConstraint: \(constraint)")
      }
    
      // not working if set here
    
      collectionViewFlowLayout.scrollDirection = .vertical
    } else {
      for constraint in self.portraitConstraints {
        constraint.isActive = true
      }
    
      for constraint in self.portraitConstraints {
        print("after: portraitConstraints: \(constraint)")
      }
    
      for constraint in self.landscapeConstraints {
        print("after: landscapeConstraint: \(constraint)")
      }
    
      collectionViewFlowLayout.scrollDirection = .horizontal
    }
    

    它可以工作,但有时我会在控制台中观察到冲突的约束错误。

    所以我的问题是,我是应该移除约束,还是仅仅禁用它们来处理方向更改?

    最后是完整代码(不长):

    import UIKit
    
    class ViewController: UICollectionViewController{
    
      let collectionViewCellIdentifier = "MyCollectionViewCellIdentifier"
      let canvas:Canvas = {
        let canvas = Canvas()
        canvas.backgroundColor = UIColor.black
        canvas.translatesAutoresizingMaskIntoConstraints=false
        canvas.alpha = 0.2
        return canvas
      } ()
    
      let photoView: UIImageView = {
        let imageView = UIImageView()
    
        imageView.image = UIImage(imageLiteralResourceName: "hongjinbao")
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill
        return imageView
      } ()
    
    
      private let myArray: NSArray = ["First","Second","Third"]
    
      var portraitConstraints = [NSLayoutConstraint]()
    
      var landscapeConstraints = [NSLayoutConstraint]()
    
      override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    
        // stack views
        view.addSubview(photoView)
        view.addSubview(canvas)
    
        collectionView?.backgroundColor = UIColor.white
        collectionView?.translatesAutoresizingMaskIntoConstraints = false
    
        collectionView?.register(PersonCollectionViewCell.self, forCellWithReuseIdentifier: collectionViewCellIdentifier)
    
    
        setupLayout()
    
        // initial adjusting orientation
    
        DispatchQueue.main.async {
    
          self.adjustLayout()
        }
    
      }
    
      override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
    
    
    
        self.adjustLayout()
    
      }
    
      private func setupLayout() {
    
        // MARK: - portrait constraints
        guard let collectionView = collectionView else {
          NSLog("failed to unwrap collectionView")
          return
    
        }
    
    
        portraitConstraints.append(photoView.topAnchor.constraint(equalTo: view.topAnchor))
        portraitConstraints.append(photoView.heightAnchor.constraint(equalTo: view.widthAnchor,   multiplier: 1.333))
        portraitConstraints.append(photoView.leadingAnchor.constraint(equalTo: view.leadingAnchor))
        portraitConstraints.append(photoView.trailingAnchor.constraint(equalTo: view.trailingAnchor))
    
        portraitConstraints.append(canvas.topAnchor.constraint(equalTo: photoView.topAnchor))
        portraitConstraints.append(canvas.bottomAnchor.constraint(equalTo: photoView.bottomAnchor))
        portraitConstraints.append(canvas.leadingAnchor.constraint(equalTo: photoView.leadingAnchor))
        portraitConstraints.append(canvas.trailingAnchor.constraint(equalTo: photoView.trailingAnchor))
    
        portraitConstraints.append(collectionView.topAnchor.constraint(equalTo: photoView.bottomAnchor))
        portraitConstraints.append(collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor))
        portraitConstraints.append(collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor))
        portraitConstraints.append(collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor))
    
        for constraint in portraitConstraints {
          constraint.isActive = false
          print("portraitConstraint: \(constraint)")
        }
    
        // MARK: - landscape constraints
    
        landscapeConstraints.append(photoView.topAnchor.constraint(equalTo: view.topAnchor))
        landscapeConstraints.append(photoView.widthAnchor.constraint(equalTo: view.heightAnchor,   multiplier: 1.333))
        landscapeConstraints.append(photoView.leadingAnchor.constraint(equalTo: view.leadingAnchor))
        landscapeConstraints.append(photoView.trailingAnchor.constraint(equalTo: view.trailingAnchor))
    
        landscapeConstraints.append(canvas.topAnchor.constraint(equalTo: photoView.topAnchor))
        landscapeConstraints.append(canvas.bottomAnchor.constraint(equalTo: photoView.bottomAnchor))
        landscapeConstraints.append(canvas.leadingAnchor.constraint(equalTo: photoView.leadingAnchor))
        landscapeConstraints.append(canvas.trailingAnchor.constraint(equalTo: photoView.trailingAnchor))
    
        landscapeConstraints.append(collectionView.topAnchor.constraint(equalTo: view.topAnchor))
        landscapeConstraints.append(collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor))
        landscapeConstraints.append(collectionView.leadingAnchor.constraint(equalTo: photoView.trailingAnchor))
        landscapeConstraints.append(collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor))
    
        for constraint in landscapeConstraints {
          constraint.isActive = false
          print("landscapeConstraint: \(constraint)")
        }
      }
    
      private func adjustLayout() {
        guard let collectionViewFlowLayout =  collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
          NSLog("failed to convert layout as flow layout")
    
          return
    
        }
    
    
        for constraint in self.portraitConstraints {
          constraint.isActive = false
          print("before: portraitConstraints: \(constraint)")
        }
    
        for constraint in self.landscapeConstraints {
          constraint.isActive = false
          print("before: landscapeConstraint: \(constraint)")
        }
    
    
        // DispatchQueue.main.async {
        if UIDevice.current.orientation.isLandscape {
          // gw: note: always disable previous rules first, then do enabling new rules
          // implications: if you enable new rule first, you will have a short time period with conflicting rules
    
    
          for constraint in self.landscapeConstraints {
            constraint.isActive = true
          }
    
          for constraint in self.portraitConstraints {
            print("after: portraitConstraints: \(constraint)")
          }
    
          for constraint in self.landscapeConstraints {
            print("after: landscapeConstraint: \(constraint)")
          }
    
    
    
          // not working if set here
    
          collectionViewFlowLayout.scrollDirection = .vertical
    
    
        } else {
    
          for constraint in self.portraitConstraints {
            constraint.isActive = true
          }
    
          for constraint in self.portraitConstraints {
            print("after: portraitConstraints: \(constraint)")
          }
    
          for constraint in self.landscapeConstraints {
            print("after: landscapeConstraint: \(constraint)")
          }
    
    
    
          collectionViewFlowLayout.scrollDirection = .horizontal
        }
        // }
    
        self.collectionView?.collectionViewLayout.invalidateLayout()
      }
    
    }
    
    
    // MARK: - UICollectionViewDataSource
    extension ViewController {
    
      override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.collectionViewCellIdentifier, for: indexPath)
        return cell
      }
    
    
      override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 13
      }
    
    
    }
    
    // MARK: - UICollectionViewDelegateFlowLayout
    extension ViewController : UICollectionViewDelegateFlowLayout {
    
      // set item size
      func collectionView(_ collectionView: UICollectionView,
                          layout collectionViewLayout: UICollectionViewLayout,
                          sizeForItemAt indexPath: IndexPath) -> CGSize {
    
        // gw: to force one row, height need to be smaller than flow height
        return CGSize(width: 200, height: collectionView.bounds.height)
      }
    }
    

    有关冲突约束的错误:

    The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
    2019-01-19 23:05:49.689599-0800 CelebScope[50076:5888826] [LayoutConstraints] Unable to simultaneously satisfy constraints.
        Probably at least one of the constraints in the following list is one you don't want. 
        Try this: 
            (1) look at each constraint and try to figure out which you don't expect; 
            (2) find the code that added the unwanted constraint or constraints and fix it. 
    (
        "<NSLayoutConstraint:0x600001ae14f0 UIImageView:0x7ffe79e0d510.width == 1.333*UICollectionViewControllerWrapperView:0x7ffe79e12190.height   (active)>",
        "<NSLayoutConstraint:0x600001ae1540 H:|-(0)-[UIImageView:0x7ffe79e0d510]   (active, names: '|':UICollectionViewControllerWrapperView:0x7ffe79e12190 )>",
        "<NSLayoutConstraint:0x600001ae17c0 H:[UIImageView:0x7ffe79e0d510]-(0)-[UICollectionView:0x7ffe7a06da00]   (active)>",
        "<NSLayoutConstraint:0x600001ae1810 UICollectionView:0x7ffe7a06da00.trailing == UICollectionViewControllerWrapperView:0x7ffe79e12190.trailing   (active)>",
        "<NSLayoutConstraint:0x600001af50e0 'UIView-Encapsulated-Layout-Height' UICollectionViewControllerWrapperView:0x7ffe79e12190.height == 736   (active)>",
        "<NSLayoutConstraint:0x600001af5090 'UIView-Encapsulated-Layout-Width' UICollectionViewControllerWrapperView:0x7ffe79e12190.width == 414   (active)>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x600001ae17c0 H:[UIImageView:0x7ffe79e0d510]-(0)-[UICollectionView:0x7ffe7a06da00]   (active)>
    

    在激活一组约束之前,我已经禁用了所有约束,但我不确定为什么它仍然报告冲突的约束。

    1 回复  |  直到 7 年前
        1
  •  3
  •   Shehata Gamal    7 年前

    我觉得没问题,所以不要删除它们,而是让它们共享

    portraitConstraints.append(canvas.topAnchor.constraint(equalTo: photoView.topAnchor))
    portraitConstraints.append(canvas.bottomAnchor.constraint(equalTo: photoView.bottomAnchor))
    portraitConstraints.append(canvas.leadingAnchor.constraint(equalTo: photoView.leadingAnchor))
    portraitConstraints.append(canvas.trailingAnchor.constraint(equalTo: photoView.trailingAnchor))
    

    &

    landscapeConstraints.append(canvas.topAnchor.constraint(equalTo: photoView.topAnchor))
    landscapeConstraints.append(canvas.bottomAnchor.constraint(equalTo: photoView.bottomAnchor))
    landscapeConstraints.append(canvas.leadingAnchor.constraint(equalTo: photoView.leadingAnchor))
    landscapeConstraints.append(canvas.trailingAnchor.constraint(equalTo: photoView.trailingAnchor))
    

    你的冲突也和你指定的景观一样

    landscapeConstraints.append(photoView.widthAnchor.constraint(equalTo: view.heightAnchor,   multiplier: 1.333)) 
    landscapeConstraints.append(photoView.leadingAnchor.constraint(equalTo: view.leadingAnchor)) 
    landscapeConstraints.append(photoView.trailingAnchor.constraint(equalTo: view.trailingAnchor))
    

    前导和尾随不等于乘数宽度,所以我猜在横向上你的意思是

    landscapeConstraints.append(photoView.topAnchor.constraint(equalTo: view.topAnchor))
    landscapeConstraints.append(photoView.widthAnchor.constraint(equalTo: view.heightAnchor,   multiplier: 1.333))
    landscapeConstraints.append(photoView.leadingAnchor.constraint(equalTo: view.leadingAnchor))
    landscapeConstraints.append(photoView.bottomAnchor.constraint(equalTo: view.bottomAnchor))