[英]AutoLayout constraints to fit view inside rectangle, preserving a certain aspect ratio (programmatically)
我想將圖像放置在應該具有特定長寬比的矩形內。 不管它是什么,它都應該找到適合矩形內部的形式。 我在情節提要中玩了一下,得到了:
帶有虛線邊框的優先級較低(250)。 這在情節提要中起作用。 但是,我需要以編程方式創建這些約束,所以我這樣嘗試了(我使用的是SnapKit ,它只是提供了更好的AutoLayout
語法。這應該是不言自明的):
let topView = UIView()
topView.translatesAutoresizingMaskIntoConstraints = false
topView.backgroundColor = .gray
view.addSubview(topView)
topView.snp.makeConstraints { (make) in
make.top.equalToSuperview()
make.left.equalToSuperview()
make.trailing.equalToSuperview()
make.height.equalTo(250)
}
// This view should have a specific aspect ratio and fit inside topView
let holderView = UIView()
holderView.translatesAutoresizingMaskIntoConstraints = false
holderView.backgroundColor = .red
topView.addSubview(holderView)
holderView.snp.makeConstraints { (make) in
make.center.equalToSuperview() // If I remove this one, there's no auto-layout issue, but then it's offset
make.edges.equalToSuperview().priority(250) // sets leading, trailing, top and bottom
make.edges.greaterThanOrEqualToSuperview().priority(1000)
make.width.equalTo(holderView.snp.height).multipliedBy(3/2)
}
如果將其粘貼到一個空的ViewController中並啟動它,則會出現以下問題:
2018-03-16 15:38:50.188867+0100 DemoProject[11298:850932] [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.
"<SnapKit.LayoutConstraint:0x6000000a7c80@ViewController.swift#24 UIView:0x7fcd82d12440.left == UIView:0x7fcd82d12640.left>",
"<SnapKit.LayoutConstraint:0x6000000a7ce0@ViewController.swift#25 UIView:0x7fcd82d12440.trailing == UIView:0x7fcd82d12640.trailing>",
"<SnapKit.LayoutConstraint:0x6000000a7d40@ViewController.swift#26 UIView:0x7fcd82d12440.height == 250.0>",
"<SnapKit.LayoutConstraint:0x6000000a7da0@ViewController.swift#35 UIView:0x7fcd8580dad0.centerX == UIView:0x7fcd82d12440.centerX>",
"<SnapKit.LayoutConstraint:0x6000000a7e00@ViewController.swift#35 UIView:0x7fcd8580dad0.centerY == UIView:0x7fcd82d12440.centerY>",
"<SnapKit.LayoutConstraint:0x6000000a8580@ViewController.swift#37 UIView:0x7fcd8580dad0.top >= UIView:0x7fcd82d12440.top>",
"<SnapKit.LayoutConstraint:0x6000000a8a60@ViewController.swift#37 UIView:0x7fcd8580dad0.right >= UIView:0x7fcd82d12440.right>",
"<SnapKit.LayoutConstraint:0x6000000a9360@ViewController.swift#38 UIView:0x7fcd8580dad0.width == UIView:0x7fcd8580dad0.height>",
"<NSLayoutConstraint:0x600000092cf0 'UIView-Encapsulated-Layout-Width' UIView:0x7fcd82d12640.width == 414 (active)>"
Will attempt to recover by breaking constraint <SnapKit.LayoutConstraint:0x6000000a9360@ViewController.swift#38 UIView:0x7fcd8580dad0.width == UIView:0x7fcd8580dad0.height>
當我刪除居中約束make.center.equalToSuperview()
時,這不會顯示。 但隨后,它放錯了位置。
情節提要和我的代碼之間有什么區別? 我不太明白。 我還使用默認的swift語法嘗試了此操作,結果完全相同。 所以我認為SnapKit
問題
有任何想法嗎? 謝謝大家的幫助。 讓我知道您是否需要更多信息。
編輯:我混合了一些東西。 這與圖像及其寬高比無關。 它只是關於一個UIView
,它應該在安裝在矩形內時保持特定的寬高比。 實際圖像將被放入該holderView
。 抱歉
好的-這是一種方法。
取子視圖的“本機”大小,計算“長寬比”比例-即適合寬度或高度到父視圖的比例,並適當縮放其他尺寸。
然后,使用centerXAnchor
和centerYAnchor
定位子視圖,並使用widthAnchor
和heightAnchor
其大小。
注意:如果要放置圖像 ,請根據圖像大小計算寬高比合適的大小,將圖像放入圖像視圖,將圖像視圖縮放模式設置為fill
,最后將約束應用於圖像視圖。
您應該能夠按原樣運行此示例。 只需在頂部使用“本機”大小值即可查看它如何將子視圖適合到超級視圖中。
public class AspectFitViewController : UIViewController {
// "native" size for the holderView
let hViewWidth: CGFloat = 700.0
let hViewHeight: CGFloat = 200.0
let topView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.blue
return v
}()
let holderView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.cyan
return v
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.bounds = CGRect(x: 0, y: 0, width: 400, height: 600)
view.backgroundColor = .yellow
// add topView
view.addSubview(topView)
// pin topView to leading / top / trailing
topView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0).isActive = true
topView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
topView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true
// explicit height for topView
topView.heightAnchor.constraint(equalToConstant: 250.0).isActive = true
// add holderView to topView
topView.addSubview(holderView)
// center X and Y
holderView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0).isActive = true
holderView.centerYAnchor.constraint(equalTo: topView.centerYAnchor, constant: 0.0).isActive = true
// holderView's width and height will be calculated in viewDidAppear
// after topView has been laid-out by the auto-layout engine
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let aspectWidth = topView.bounds.size.width / hViewWidth
let aspectHeight = topView.bounds.size.height / hViewHeight
let aspectFit = min(aspectWidth, aspectHeight)
let newWidth = hViewWidth * aspectFit
let newHeight = hViewHeight * aspectFit
holderView.widthAnchor.constraint(equalToConstant: newWidth).isActive = true
holderView.heightAnchor.constraint(equalToConstant: newHeight).isActive = true
}
}
編輯:
在澄清之后……這只能通過約束來實現。 關鍵是“ Priority 1000” top
和leading
約束必須為.greaterThanOrEqual
等於零,而bottom
和trailing
約束則必須為.lessThanOrEqual
等於零。
public class AspectFitViewController : UIViewController {
// "native" size for the holderView
let hViewWidth: CGFloat = 700.0
let hViewHeight: CGFloat = 200.0
let topView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.blue
return v
}()
let holderView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor.cyan
return v
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.bounds = CGRect(x: 0, y: 0, width: 400, height: 600)
view.backgroundColor = .yellow
// add topView
view.addSubview(topView)
// pin topView to leading / top / trailing
topView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0).isActive = true
topView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
topView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true
// explicit height for topView
topView.heightAnchor.constraint(equalToConstant: 250.0).isActive = true
// add holderView to topView
topView.addSubview(holderView)
// center X and Y
holderView.centerXAnchor.constraint(equalTo: topView.centerXAnchor, constant: 0.0).isActive = true
holderView.centerYAnchor.constraint(equalTo: topView.centerYAnchor, constant: 0.0).isActive = true
// aspect ratio size
holderView.widthAnchor.constraint(equalTo: holderView.heightAnchor, multiplier: hViewWidth / hViewHeight).isActive = true
// two constraints for each side...
// the .equal constraints need .defaultLow priority
// top and leading constraints must be .greaterThanOrEqual to 0
// bottom and trailing constraints must be .lessThanOrEqual to 0
let topA = NSLayoutConstraint(item: holderView, attribute: .top, relatedBy: .greaterThanOrEqual, toItem: topView, attribute: .top, multiplier: 1.0, constant: 0.0)
let topB = NSLayoutConstraint(item: holderView, attribute: .top, relatedBy: .equal, toItem: topView, attribute: .top, multiplier: 1.0, constant: 0.0)
let bottomA = NSLayoutConstraint(item: holderView, attribute: .bottom, relatedBy: .lessThanOrEqual, toItem: topView, attribute: .bottom, multiplier: 1.0, constant: 0.0)
let bottomB = NSLayoutConstraint(item: holderView, attribute: .bottom, relatedBy: .equal, toItem: topView, attribute: .bottom, multiplier: 1.0, constant: 0.0)
let leadingA = NSLayoutConstraint(item: holderView, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: topView, attribute: .leading, multiplier: 1.0, constant: 0.0)
let leadingB = NSLayoutConstraint(item: holderView, attribute: .leading, relatedBy: .equal, toItem: topView, attribute: .leading, multiplier: 1.0, constant: 0.0)
let trailingA = NSLayoutConstraint(item: holderView, attribute: .trailing, relatedBy: .lessThanOrEqual, toItem: topView, attribute: .trailing, multiplier: 1.0, constant: 0.0)
let trailingB = NSLayoutConstraint(item: holderView, attribute: .trailing, relatedBy: .equal, toItem: topView, attribute: .trailing, multiplier: 1.0, constant: 0.0)
topB.priority = .defaultLow
bottomB.priority = .defaultLow
leadingB.priority = .defaultLow
trailingB.priority = .defaultLow
NSLayoutConstraint.activate([
topA, topB,
bottomA, bottomB,
leadingA, leadingB,
trailingA, trailingB
])
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.