ios Swift 中的尾随和前导约束以编程方式 (NSLayoutConstraints)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39733551/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Trailing and Leading constraints in Swift programmatically (NSLayoutConstraints)
提问by BlackBox
I'm adding from a xib a view into my ViewController. Then I'm putting its constraints to actually fit it
我正在从 xib 添加一个视图到我的 ViewController 中。然后我将其约束条件实际适合它
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
...
...
view!.addSubview(gamePreview)
gamePreview.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 9.0, *) {
// Pin the leading edge of myView to the margin's leading edge
gamePreview.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor).active = true
//Pin the trailing edge of myView to the margin's trailing edge
gamePreview.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor).active = true
} else {
// Fallback on earlier versions
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .TrailingMargin, relatedBy: .Equal, toItem: view, attribute: .TrailingMargin, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .LeadingMargin, relatedBy: .Equal, toItem: view, attribute: .LeadingMargin, multiplier: 1, constant: 0))
}
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))
}
What i'm trying to do: To actually fit my view constraining it to top, leading, trailing to ViewController's view, and with a prefixed height. The view I'm adding to main view has its own transparent-background view, so no need of margin (the view is meant to be device's width size, so).
我正在尝试做的是:真正适合我的视图,将其限制在顶部、引导、拖尾到 ViewController 的视图,并带有前缀高度。我添加到主视图的视图有它自己的透明背景视图,所以不需要边距(视图意味着设备的宽度大小,所以)。
I've placed 2 couples of lines that would be supposed to be equal (in my attempts), with the if, because the first 2 lines in if are actually available in iOS9> only, while I'm attempting to do the same thing in the else statement, for every device (starting from iOS 8).
我已经放置了 2 对应该相等的行(在我的尝试中),带有 if,因为 if 中的前 2 行实际上仅在 iOS9> 中可用,而我正在尝试做同样的事情在 else 语句中,对于每个设备(从 iOS 8 开始)。
This is what I get:
这就是我得到的:
iOS9+ at left, iOS8+ at right. Transparent background was colored red to show what happens (don't mind at different height in the images, they're equal height in app, instead look at added margins at left and right)
左侧为 iOS9+,右侧为 iOS8+。透明背景被染成红色以显示发生了什么(不要介意图像中的不同高度,它们在应用程序中的高度相同,而是查看左右添加的边距)
I also tried AutoLayoutDSL-Swift
, but doesn't help, I'm not expert with it but every attempt made only things worse.
我也试过AutoLayoutDSL-Swift
,但没有帮助,我不是专家,但每次尝试只会让事情变得更糟。
How can I write those constraints using classic NSLayoutConstraints methods, and how could I write all in a better way with a framework like AutoLayoutDSL or a fork of it? (or an alternative, though now mostly I'm concerned on official libs)
如何使用经典的 NSLayoutConstraints 方法编写这些约束,以及如何使用 AutoLayoutDSL 等框架或它的分支以更好的方式编写所有约束?(或另一种选择,尽管现在我最关心的是官方库)
回答by Paulw11
You need to use the leading
and trailing
attributes, not the leadingMargin
and trailingMargin
attributes:
您需要使用leading
andtrailing
属性,而不是leadingMargin
andtrailingMargin
属性:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
...
...
view!.addSubview(gamePreview)
gamePreview.translatesAutoresizingMaskIntoConstraints = false
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))
}
回答by BlackBox
Guessed it thanks to @Paulw11... this is the solution
感谢@Paulw11 猜到了……这就是解决方案
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: gamePreview, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 131))
Also I managed to rewrite it as it follows using AutoLayoutDSL-Swift
for anyone interested
我也设法重写它,如下所示,AutoLayoutDSL-Swift
供任何感兴趣的人使用
view => gamePreview.trailing == view.trailing
=> gamePreview.leading == view.leading
=> gamePreview.height == 131
=> gamePreview.top == view.top + self.navigationController!.navigationBar.bounds.height + UIApplication.sharedApplication().statusBarFrame.size.height
The last line is a little long because view.top refers to the really view top and doesn't consider padding added by both statusBar and navigationBar heights. They could be replaced as constants, waiting if someone comes up with a more elegant solution.
最后一行有点长,因为 view.top 指的是真正的视图顶部,并且不考虑由 statusBar 和 navigationBar 高度添加的填充。它们可以被替换为常量,等待有人提出更优雅的解决方案。
回答by SWARTY
SWIFT 4 Update
SWIFT 4 更新
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: stackView, attribute: .trailing, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: stackView, attribute: .leading, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.addLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0))
stackView.addConstraint(NSLayoutConstraint(item: stackView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute,multiplier: 1,
constant: 131))
回答by Septronic
Why don't you use constraint activation? You could do this:
为什么不使用约束激活?你可以这样做:
var anchors = [NSLayoutConstraint]()
anchors.append(view.topAnchor.constraint(equalTo: gamePreview.topAnchor, constant: 0))
anchors.append(view.leadingAnchor.constraint(equalTo: gamePreview.leadingAnchor, constant: 0))
anchors.append(view.trailingAnchor.constraint(equalTo: gamePreview.trailingAnchor, constant: 0))
anchors.append(view.heightAnchor.constraint(equalTo: gamePreview.heightAnchor, multiplier: 1, constant: 131))
NSLayoutConstraint.activate(anchors)
This is another. Hope it helps. You could swap view
and gamePreview
depending on the parent / child, or you could create a general helper extension for your constraints. I found one in a tutorial provided by a YouTuber Hereand slightly modified it to accept leading/trailing, left/right constraints. You could also add safeLayoutGuide
to the below as well, which makes it future-friendly. Something like this:
这是另一个。希望能帮助到你。您可以交换view
并gamePreview
根据父/子,或者您可以为您的约束创建一个通用的辅助扩展。我在 YouTuber Here提供的教程中找到了一个,并稍微修改了它以接受前导/尾随、左/右约束。您也可以添加safeLayoutGuide
到以下内容中,这使其对未来友好。像这样的东西:
func anchor(_ top: UIView? = nil, left: UIView? = nil, bottom: UIView? = nil, right: UIView? = nil, width: UIView? = nil, height: UIView? = nil, topConstant: CGFloat = 0, leftConstant: CGFloat = 0, bottomConstant: CGFloat = 0, rightConstant: CGFloat = 0, widthConstant: CGFloat = 0, heightConstant: CGFloat = 0, isForLeading: Bool = false) -> [NSLayoutConstraint] {
translatesAutoresizingMaskIntoConstraints = false
var anchors = [NSLayoutConstraint]()
if #available(iOS 11.0, *) {
if let top = top {
anchors.append(topAnchor.constraint(equalTo: top.safeAreaLayoutGuide.topAnchor, constant: topConstant))
}
if let left = left {
if isForLeading {
anchors.append(leadingAnchor.constraint(equalTo: left.safeAreaLayoutGuide.leadingAnchor, constant: leftConstant))
} else {
anchors.append(leftAnchor.constraint(equalTo: left.safeAreaLayoutGuide.leftAnchor, constant: leftConstant))
}
}
if let bottom = bottom {
anchors.append(bottomAnchor.constraint(equalTo: bottom.safeAreaLayoutGuide.bottomAnchor, constant: -bottomConstant))
}
if let right = right {
if isForLeading {
anchors.append(trailingAnchor.constraint(equalTo: right.safeAreaLayoutGuide.leadingAnchor, constant: rightConstant))
} else {
anchors.append(rightAnchor.constraint(equalTo: right.safeAreaLayoutGuide.rightAnchor, constant: -rightConstant))
}
}
if let width = width {
anchors.append(widthAnchor.constraint(equalTo: width.safeAreaLayoutGuide.widthAnchor, multiplier: 1, constant: widthConstant))
} else if widthConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
}
if let height = height {
anchors.append(heightAnchor.constraint(equalTo: height.safeAreaLayoutGuide.heightAnchor, multiplier: 1, constant: widthConstant))
} else if heightConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: heightConstant))
}
anchors.forEach({ _ = view.anchor(gamePreview, left: gamePreview, right: gamePreview, height: gamePreview, heightConstant: 131, isForLeading: true)
.isActive = true})
} else {
if let top = top {
anchors.append(topAnchor.constraint(equalTo: top.topAnchor, constant: topConstant))
}
if let left = left {
if isForLeading {
anchors.append(leadingAnchor.constraint(equalTo: left.leadingAnchor, constant: leftConstant))
} else {
anchors.append(leftAnchor.constraint(equalTo: left.leftAnchor, constant: leftConstant))
}
}
if let bottom = bottom {
anchors.append(bottomAnchor.constraint(equalTo: bottom.bottomAnchor, constant: -bottomConstant))
}
if let right = right {
if isForLeading {
anchors.append(trailingAnchor.constraint(equalTo: right.leadingAnchor, constant: rightConstant))
} else {
anchors.append(rightAnchor.constraint(equalTo: right.rightAnchor, constant: -rightConstant))
}
}
if let width = width {
anchors.append(widthAnchor.constraint(equalTo: width.widthAnchor, multiplier: 1, constant: widthConstant))
} else if widthConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: widthConstant))
}
if let height = height {
anchors.append(heightAnchor.constraint(equalTo: height.heightAnchor, multiplier: 1, constant: widthConstant))
} else if heightConstant > 0 {
anchors.append(widthAnchor.constraint(equalToConstant: heightConstant))
}
anchors.forEach({##代码##.isActive = true})
}
return anchors
}
And then call it like this:
然后像这样调用它:
##代码##