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

时间:2019-01-20 15:03:32

标签: ios swift autolayout

我正在以编程方式创建自适应布局。我的计划是要有两组约束:portraitConstraintslandscapeConstraints。当设备更改方向(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 个答案:

答案 0 :(得分:3)

我认为没有问题,所以不要删除它们,而要使其共享

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))