iOS Auto Layout在空间不足时会保持最小的间隙,在空间足够时会占用尽可能多的空间

问题描述

enter image description here

这是布局。顶部的标签文本可能会变得很长,我希望当当前有足够的空间时,在底部选择的标签距离底部的按钮至少40 pt,而在没有足够的空间时至少10 pt。尝试进行优先级排序,但似乎不起作用,请帮助我。这是我的代码的一部分。

buttonView.snp.updateConstraints { (make) in
  make.leading.bottom.trailing.equalToSuperview()
  make.height.equalTo(buttonView.intrinsicContentSize.height)
}

contentLabel.snp.updateConstraints { (make) in
  make.top.greaterThanorEqualTo(headerImageView.snp.bottom).offset(10.dp)
  make.leading.equalTo(50.dph)
  make.trailing.equalTo(-50.dph)
}

codeTextField.snp.updateConstraints { (make) in
  make.top.equalTo(contentLabel.snp.bottom).offset(10.dp)
  make.leading.equalTo(46.5.dp)
  make.trailing.equalTo(-46.5.dp)
  make.height.equalTo(50.dp)
}

tipsLabel.snp.updateConstraints { (make) in
  make.top.equalTo(codeTextField.snp.bottom).offset(10.dp)
  make.width.centerX.equalTo(codeTextField)
  make.bottom.equalTo(buttonView.snp.top).offset(-10.dp)
  make.bottom.greaterThanorEqualTo(buttonView.snp.top).offset(-40.dp)
}

Click here下载演示项目。

解决方法

耦合事物...

首先,您想在“ tips”标签上设置“ Compression Resistance”,以免压缩不存在。

第二,当给对象一个约束时,例如:

bottom of viewA greaterThanOrEqual to -40 from top of viewB
bottom of viewA lessThanOrEqual to -10 from top of viewB

您尚未提供完整的布局。介于-40和-10之间的 ANY 值是有效的,因此自动布局无法理解您的真正需求。

要解决此问题,根据您的描述,您希望 为40点,并且至少为 10点。因此,您需要两个约束条件:

  • viewA at least -10的底部(从viewB顶部开始)
  • 视图A的底部equalTo从视图B的顶部-40- 除非没有足够的空间 -因此给它的Priority小于required

尝试使用此代码-我在进行更改的地方添加了// MARK: DonMag ...条评论:

class MyView: UIView {

  override class var requiresConstraintBasedLayout: Bool { return true }

    // MARK: DonMag - a few varied length contentLabel strings
    private var idx: Int = 0
    private let examples: [String] = [
        "Check your email.","Please check your email to activate your account.\n\nEnter the activation code:","Lorem ipsum dolor sit amet,consectetur adipiscing elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,sunt in culpa qui officia deserunt mollit anim id est laborum.",]

  private let headerView = UIButton().then {
    $0.backgroundColor = .purple
    $0.setTitle("Tap me!",for: .normal)
  }

  private let contentLabel = UILabel().then {
    $0.font = .systemFont(ofSize: 21)
    $0.numberOfLines = 0
    $0.setContentHuggingPriority(.required,for: .vertical)
    
    // MARK: DonMag - so we can see its frame
    $0.textAlignment = .center
    $0.backgroundColor = .cyan

  }

  private let codeTextField = UITextField().then {
    $0.placeholder = "Enter code"
    $0.backgroundColor = UIColor.white.withAlphaComponent(0.2)
    $0.layer.cornerRadius = 8
    $0.layer.borderWidth = 1
    $0.layer.borderColor = UIColor.lightGray.cgColor
    $0.textAlignment = .center
    $0.font = .systemFont(ofSize: 21)
    $0.keyboardType = .numberPad
  }

  private let tipsLabel = UILabel().then {
    $0.font = .systemFont(ofSize: 12)
    $0.adjustsFontSizeToFitWidth = true
    $0.minimumScaleFactor = 0.2
    $0.textAlignment = .center
    $0.text = "If you have not received the code,tap 'resend'."
    $0.setContentHuggingPriority(.required,for: .vertical)
    
    // MARK: DonMag - prevent label from being compressed vertically
    $0.setContentCompressionResistancePriority(.required,for: .vertical)
    // MARK: DonMag - so we can see its frame
    $0.backgroundColor = .yellow

  }

  private let bottomView = UIView().then {
    $0.backgroundColor = .red
  }

  override init(frame: CGRect) {
    super.init(frame: frame)

    backgroundColor = .white

    addSubview(headerView)
    addSubview(contentLabel)
    addSubview(codeTextField)
    addSubview(tipsLabel)
    addSubview(bottomView)

    headerView.addTarget(self,action: #selector(headerViewDidTapped),for: .touchUpInside)
    headerViewDidTapped()
  }

  override func updateConstraints() {
    headerView.snp.updateConstraints { (make) in
      make.leading.trailing.top.equalToSuperview()
      make.height.equalToSuperview().multipliedBy(0.454)
    }

    bottomView.snp.updateConstraints { (make) in
      make.leading.bottom.trailing.equalToSuperview()
      make.height.equalTo(121)
    }

    contentLabel.snp.updateConstraints { (make) in
      make.top.greaterThanOrEqualTo(headerView.snp.bottom).offset(10)
      make.leading.equalTo(50)
      make.trailing.equalTo(-50)
    }

    codeTextField.snp.updateConstraints { (make) in
      make.top.equalTo(contentLabel.snp.bottom).offset(10)
      make.leading.equalTo(46.5)
      make.trailing.equalTo(-46.5)
      make.height.equalTo(50)
    }

    tipsLabel.snp.updateConstraints { (make) in
      make.top.equalTo(codeTextField.snp.bottom).offset(10)
      make.width.centerX.equalTo(codeTextField)
      make.bottom.greaterThanOrEqualTo(bottomView.snp.top).offset(-40)
        
      // MARK: DonMag - tipsLabel bottom should be equalTo -40 with medium priority (so it can be overridden by auto-layout when needed)
      //make.bottom.greaterThanOrEqualTo(bottomView.snp.top).offset(-40)
      make.bottom.equalTo(bottomView.snp.top).offset(-40).priority(.medium)
        
      make.bottom.lessThanOrEqualTo(bottomView.snp.top).offset(-10)
    }

    super.updateConstraints()
  }

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  @objc
  private func headerViewDidTapped() {

    // MARK: DonMag - get example string for contentLabel
    let text = examples[idx % examples.count]
    idx += 1

    contentLabel.text = text
    setNeedsUpdateConstraints()

    UIView.animate(withDuration: 0.1) {
      self.layoutIfNeeded()
    }
  }

}
,

设置两个约束:

make.bottom.greaterThanOrEqualTo(buttonView.snp.top).offset(-40)
make.bottom.lessThanOrEqualTo(buttonView.snp.top).offset(-10)
make.height.equalTo(21)

并更改内容标签约束的优先级:

      make.top.greaterThanOrEqualTo(headerView.snp.bottom).offset(10).priority(751)