iOS Auto Layout 在 space 不够时保持最小间距,在 space 足够时尽可能多地占用 space
iOS Auto Layout keeps the minimum gap when the space is not enough, and takes up as much space as possible when the space is enough
这是布局。顶部的 Label 文本可能会变得很长,我希望底部选择的 Label 在 space 足够的时候距离底部按钮至少 40 pt,没有时至少 10 pt enough space.I 已尝试确定优先级,但似乎不起作用,请帮助我。这是我的部分代码。
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 下载演示项目。
做两个约束:
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)
两件事...
首先,您想在“提示”标签上设置压缩阻力,这样它就不会被挤压而消失。
其次,当你给一个对象约束时,比如:
bottom of viewA greaterThanOrEqual to -40 from top of viewB
bottom of viewA lessThanOrEqual to -10 from top of viewB
你没有给出完整的布局。 ANY -40 和 -10 之间的值都是有效的,所以 auto-layout 不明白你真正想要什么。
要解决这个问题,根据您的描述,您需要 40 分如果可能,并且至少 10 分。所以你想要两个约束说:
- viewA 底部
at least
-10 从 viewB 顶部
- viewA 底部
equalTo
-40 从 viewB 顶部 - 除非没有足够的 space - 所以给它 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 {
[=11=].backgroundColor = .purple
[=11=].setTitle("Tap me!", for: .normal)
}
private let contentLabel = UILabel().then {
[=11=].font = .systemFont(ofSize: 21)
[=11=].numberOfLines = 0
[=11=].setContentHuggingPriority(.required, for: .vertical)
// MARK: DonMag - so we can see its frame
[=11=].textAlignment = .center
[=11=].backgroundColor = .cyan
}
private let codeTextField = UITextField().then {
[=11=].placeholder = "Enter code"
[=11=].backgroundColor = UIColor.white.withAlphaComponent(0.2)
[=11=].layer.cornerRadius = 8
[=11=].layer.borderWidth = 1
[=11=].layer.borderColor = UIColor.lightGray.cgColor
[=11=].textAlignment = .center
[=11=].font = .systemFont(ofSize: 21)
[=11=].keyboardType = .numberPad
}
private let tipsLabel = UILabel().then {
[=11=].font = .systemFont(ofSize: 12)
[=11=].adjustsFontSizeToFitWidth = true
[=11=].minimumScaleFactor = 0.2
[=11=].textAlignment = .center
[=11=].text = "If you have not received the code, tap 'resend'."
[=11=].setContentHuggingPriority(.required, for: .vertical)
// MARK: DonMag - prevent label from being compressed vertically
[=11=].setContentCompressionResistancePriority(.required, for: .vertical)
// MARK: DonMag - so we can see its frame
[=11=].backgroundColor = .yellow
}
private let bottomView = UIView().then {
[=11=].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()
}
}
}
这是布局。顶部的 Label 文本可能会变得很长,我希望底部选择的 Label 在 space 足够的时候距离底部按钮至少 40 pt,没有时至少 10 pt enough space.I 已尝试确定优先级,但似乎不起作用,请帮助我。这是我的部分代码。
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 下载演示项目。
做两个约束:
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)
两件事...
首先,您想在“提示”标签上设置压缩阻力,这样它就不会被挤压而消失。
其次,当你给一个对象约束时,比如:
bottom of viewA greaterThanOrEqual to -40 from top of viewB
bottom of viewA lessThanOrEqual to -10 from top of viewB
你没有给出完整的布局。 ANY -40 和 -10 之间的值都是有效的,所以 auto-layout 不明白你真正想要什么。
要解决这个问题,根据您的描述,您需要 40 分如果可能,并且至少 10 分。所以你想要两个约束说:
- viewA 底部
at least
-10 从 viewB 顶部 - viewA 底部
equalTo
-40 从 viewB 顶部 - 除非没有足够的 space - 所以给它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 {
[=11=].backgroundColor = .purple
[=11=].setTitle("Tap me!", for: .normal)
}
private let contentLabel = UILabel().then {
[=11=].font = .systemFont(ofSize: 21)
[=11=].numberOfLines = 0
[=11=].setContentHuggingPriority(.required, for: .vertical)
// MARK: DonMag - so we can see its frame
[=11=].textAlignment = .center
[=11=].backgroundColor = .cyan
}
private let codeTextField = UITextField().then {
[=11=].placeholder = "Enter code"
[=11=].backgroundColor = UIColor.white.withAlphaComponent(0.2)
[=11=].layer.cornerRadius = 8
[=11=].layer.borderWidth = 1
[=11=].layer.borderColor = UIColor.lightGray.cgColor
[=11=].textAlignment = .center
[=11=].font = .systemFont(ofSize: 21)
[=11=].keyboardType = .numberPad
}
private let tipsLabel = UILabel().then {
[=11=].font = .systemFont(ofSize: 12)
[=11=].adjustsFontSizeToFitWidth = true
[=11=].minimumScaleFactor = 0.2
[=11=].textAlignment = .center
[=11=].text = "If you have not received the code, tap 'resend'."
[=11=].setContentHuggingPriority(.required, for: .vertical)
// MARK: DonMag - prevent label from being compressed vertically
[=11=].setContentCompressionResistancePriority(.required, for: .vertical)
// MARK: DonMag - so we can see its frame
[=11=].backgroundColor = .yellow
}
private let bottomView = UIView().then {
[=11=].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()
}
}
}