Swift 中的尾随和前导约束以编程方式 (NSLayoutConstraints)
Posted
技术标签:
【中文标题】Swift 中的尾随和前导约束以编程方式 (NSLayoutConstraints)【英文标题】:Trailing and Leading constraints in Swift programmatically (NSLayoutConstraints) 【发布时间】:2016-09-27 20:16:06 【问题描述】:我正在将一个 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))
我正在尝试做的事情:真正适合我的视图,将其限制在顶部、前导、尾随到 ViewController 的视图,并具有前缀高度。我添加到主视图的视图有自己的透明背景视图,所以不需要边距(视图是设备的宽度大小,所以)。
我在 if 中放置了两对应该相等的行(在我的尝试中),因为 if 中的前 2 行实际上仅在 iOS9 中可用>,而我正在尝试这样做else 语句中的相同内容,适用于所有设备(从 iOS 8 开始)。
这是我得到的:
左边是iOS9+,右边是iOS8+。透明背景被涂成红色以显示发生了什么(不要介意图像中的不同高度,它们在应用程序中是相同的高度,而不是查看左右添加的边距)
我也尝试了AutoLayoutDSL-Swift
,但没有帮助,我不是这方面的专家,但每次尝试只会让事情变得更糟。
如何使用经典的 NSLayoutConstraints 方法编写这些约束,以及如何使用 AutoLayoutDSL 之类的框架或其分支以更好的方式编写所有这些约束? (或替代方案,尽管现在我主要关注官方库)
【问题讨论】:
在 iOS 9 之前的代码中,您使用了leadingMargin
和 trailingMargin
,因此您获得了利润。请改用leading
和trailing
另外,如果您进行了更改,那么相同的代码将适用于所有版本,iOS 8 及更高版本
有效!您还介意告诉我如何使用 AutoLayoutDSL-Swift 作为顶部锚点,特别是导航栏...我尝试了view => gamePreview.top == view.top
,以完成我进入倒数第二个约束的操作,但视图位于导航栏下方。 ..这将帮助我将所有这些臃肿的代码减少到由 4 分割的行中。我尝试将偏移量与self.navigationController!.navigationBar.height
的总和相加,但它会产生编译错误,而它可以对实际数字进行数字化(如 30) .
抱歉,不知道那是什么。我几乎只使用故事板
@Paulw11 我知道是对的,但是因为我知道如何让它在 iOS9 及更高版本中工作,我在问题中添加了该代码以帮助人们更容易地猜测我愿意做什么,并且确实有效。我想知道为什么我没有早点提出 view => gamePreview.trailing == view.trailing ... 我担心左右属性,它一直在失败
【参考方案1】:
您需要使用leading
和trailing
属性,而不是leadingMargin
和trailingMargin
属性:
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))
【讨论】:
我如何获得该视图的当前约束?你知道吗??我找不到任何好的解决方案 “那个观点”是什么意思。通常,您在创建约束时会将对约束的引用存储在属性中,或者如果您使用情节提要,则使用 IBoutlet。 假设我不想要该视图约束的任何 IBOutlet 引用,并且 UIView.constraints 的数组属性返回一个数组。如何在不创建引用属性的情况下获得前导、尾随、顶部等?? 您必须遍历数组并尝试找到正确的约束。保持参考要简单得多。【参考方案2】:感谢@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))
我还设法使用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
最后一行有点长,因为 view.top 指的是真正的视图顶部,并且不考虑由 statusBar 和 navigationBar 高度添加的填充。它们可以被替换为常量,等待有人提出更优雅的解决方案。
【讨论】:
【参考方案3】: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))
【讨论】:
【参考方案4】:为什么不使用约束激活?你可以这样做:
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)
这是另一个。希望能帮助到你。你可以根据父/子交换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($0.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($0.isActive = true)
return anchors
然后这样称呼它:
_ = view.anchor(gamePreview, left: gamePreview, right: gamePreview, height: gamePreview, heightConstant: 131, isForLeading: true)
【讨论】:
以上是关于Swift 中的尾随和前导约束以编程方式 (NSLayoutConstraints)的主要内容,如果未能解决你的问题,请参考以下文章