宽度和高度是否等于以编程方式使用自动布局的superView?


83

我一直在网上寻找很多片段,但仍然找不到解决问题的答案。我的问题是我有一个scrollView(SV),我想以编程方式在scrollView(SV)内添加一个按钮,使其具有与superview相同的宽度和高度,即scrollView(SV),以便在用户旋转设备按钮时具有相同的框架scrollView(SV)。如何做NSLayout / NSLayoutConstraint?谢谢

Answers:


125

如果有人正在寻找Swift解决方案–我将创建一个Swift扩展,UIView每次您想要将子视图框架绑定到其超级视图边界时扩展将为您提供帮助:

斯威夫特2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[subview]-0-|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

斯威夫特3:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }
}

Swift 4.2:

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0).isActive = true
        self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0).isActive = true
        self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0).isActive = true
        self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0).isActive = true

    }
}

然后像这样简单地调用它

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindFrameToSuperviewBounds()

使用.xib创建自定义UIView时,应在self.addSubview(self.view)之后在“所需的init?(编码器aDecoder)”中调用bindFrameToSuperviewBounds
user1603721

值得一提的是,使用视觉格式的解决方案并不安全。例如,如果要在显示导航栏和工具栏的导航控制器内部的视图中调用此视图,则该视图将在导航栏下方和工具栏下方(如果向下那么远)。
安迪·伊瓦涅斯

这也可以作为Swift 5的解决方案。我无法使用AutoLayout使自定义子视图适应parentView的大小。一旦添加子视图,就可以使用它来作为一种魅力。
toni_piu

Swift 4.2解决方案效果很好。您甚至可以通过删除constant: 0零件来使其更短一些。
Zyphrax

69

我不确定这是否是最有效的方法,但确实有效。

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
// initialize


[coverForScrolView addSubview:button];

NSLayoutConstraint *width =[NSLayoutConstraint
                                    constraintWithItem:button
                                    attribute:NSLayoutAttributeWidth
                                    relatedBy:0
                                    toItem:coverForScrolView
                                    attribute:NSLayoutAttributeWidth
                                    multiplier:1.0
                                    constant:0];
NSLayoutConstraint *height =[NSLayoutConstraint
                                     constraintWithItem:button
                                     attribute:NSLayoutAttributeHeight
                                     relatedBy:0
                                     toItem:coverForScrolView
                                     attribute:NSLayoutAttributeHeight
                                     multiplier:1.0
                                     constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint
                                   constraintWithItem:button
                                   attribute:NSLayoutAttributeTop
                                   relatedBy:NSLayoutRelationEqual
                                   toItem:coverForScrolView
                                   attribute:NSLayoutAttributeTop
                                   multiplier:1.0f
                                   constant:0.f];
NSLayoutConstraint *leading = [NSLayoutConstraint
                                       constraintWithItem:button
                                       attribute:NSLayoutAttributeLeading
                                       relatedBy:NSLayoutRelationEqual
                                       toItem:coverForScrolView
                                       attribute:NSLayoutAttributeLeading
                                       multiplier:1.0f
                                       constant:0.f];
[coverForScrolView addConstraint:width];
[coverForScrolView addConstraint:height];
[coverForScrolView addConstraint:top];
[coverForScrolView addConstraint:leading];

4
使用起来效率会更高NSLayoutConstraint.activateConstraints([width, height, top, leading])
Berik

您可以使用[coverForScrolView addConstraints:@[width, height, top, leading]];
Islam Q.

1
值得注意的是(几年后),这段代码已经过时了。现在添加约束要容易得多-请参阅下面的任何2017年答案
Fattie

49

该链接可以帮助您,按照说明进行操作:http : //www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

编辑:

使用以下代码段,其中subview是您的subivew。

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-0-[subview]-0-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(subview)]];

4
在这种情况下,视觉格式可以同时V:|[subview]|H:|[subview]|
古斯塔沃·巴博萨

4
值得注意的是(几年后),这段代码已经过时了。现在添加约束要容易得多-请参阅下面的任何2017年答案
Fattie

18

addConstraintremoveConstraintUIView的方法将被弃用,因此值得使用“约束创建便利”:

view.topAnchor.constraint(equalTo: superView.topAnchor, constant: 0).isActive = true
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: 0).isActive = true
view.leadingAnchor.constraint(equalTo: superView.leadingAnchor, constant: 0).isActive = true
view.trailingAnchor.constraint(equalTo: superView.trailingAnchor, constant: 0).isActive = true

这很好。您甚至可以通过删除constant: 0零件来使其更短一些。
Zyphrax

8

方法1:通过UIView扩展

这是Swift 3+中一种更实用的方法,带有先决条件,而不是先决条件(在控制台中很容易消失)。这将报告程序员错误,因为构建失败。print

将此扩展添加到您的项目中:

extension UIView {
    /// Adds constraints to the superview so that this view has same size and position.
    /// Note: This fails the build if the `superview` is `nil` – add it as a subview before calling this.
    func bindEdgesToSuperview() {
        guard let superview = superview else {
            preconditionFailure("`superview` was nil – call `addSubview(view: UIView)` before calling `bindEdgesToSuperview()` to fix this.")
        }
        translatesAutoresizingMaskIntoConstraints = false
        ["H:|-0-[subview]-0-|", "V:|-0-[subview]-0-|"].forEach { visualFormat in
            superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: visualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        }
    }
}

现在简单地这样称呼它

// after adding as a subview, e.g. `view.addSubview(subview)`
subview.bindEdgesToSuperview()

请注意,上述方法已经集成到我的HandyUIKit框架中,该框架还向您的项目中添加了更多方便的UI帮助器。


方法2:使用框架

如果您在项目中经常使用程序约束,那么我建议您签出SnapKit。它使处理约束变得更加容易,并且不易出错

按照文档中的安装说明将SnapKit包含到您的项目中。然后导入到您的Swift文件的顶部:

import SnapKit

现在,您可以通过以下方法实现相同的目的:

subview.snp.makeConstraints { make in
    make.edges.equalToSuperview()
}

6

斯威夫特3:

import UIKit

extension UIView {

    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    }

}

如果仅更改代码以使其与Swift 3兼容,则应编辑原始张贴者答案,而不要发布新答案(因为这不会改变原始张贴者意图)。如果您没有足够的分数进行编辑,请在原始帖子上发表评论,并提示需要遵守Swift 3所做的更改。原始海报(或其他看到您评论的人)可能会更新答案。这样,我们可以使线程保持干净,避免重复的答案和不推荐使用的代码。
Jeehut,2016年

嘿@Dschee-我完全同意你的观点,但是我们“错了”。网站上的“共识”观点是好是坏,与您在此处表达的观点相反。 meta.stackoverflow.com/questions/339024/… (我一直不理会共识决定,做一些明智的事情,然后在mods中遇到麻烦:))
Fattie

2

Swift 4使用NSLayoutConstraint

footerBoardImageView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint  = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.width, multiplier: 1, constant: 0)
let heightConstraint = NSLayoutConstraint(item: yourview, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: superview, attribute: NSLayoutAttribute.height, multiplier: 1, constant: 0)
superview.addConstraints([widthConstraint, heightConstraint])

1

作为补充回答,对于不反对包含第三方库的读者PureLayout库提供了一种做到这一点的方法。一旦安装了库,就很简单

myView.autoPinEdgesToSuperviewEdges()

还有其他库也可以根据口味提供类似的功能,例如。石工制图


0

作为@Dschee解决方案的后续措施,以下是swift 3.0语法:(请注意:这不是我的解决方案,我刚刚针对Swift 3.0对其进行了修复)

extension UIView {

    /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
    /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
    func bindFrameToSuperviewBounds() {
        guard let superview = self.superview else {
            print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
            return
        }

        self.translatesAutoresizingMaskIntoConstraints = false
        superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
    superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[subview]-0-|", options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
}

1
如果仅更改代码以使其与Swift 3兼容,则应编辑原始张贴者答案,而不要发布新答案(因为这不会改变原始张贴者意图)。如果您没有足够的分数进行编辑,请在原始帖子上发表评论,并提示需要遵守Swift 3所做的更改。原始海报(或其他看到您评论的人)可能会更新答案。这样,我们就可以使线程避免重复的答案和不推荐使用的代码。
Jeehut

我同意你的看法完全@Dschee,但(对我来说,荒谬)上元评论说:“我们不这样做,就那么” ... meta.stackoverflow.com/questions/339024/...
Fattie

@JoeBlow阅读链接背后的讨论后,我实际上认为这也是有道理的。我同意帕特里克·豪(PatrickHaugh)对这个问题的评论,尽管应该给出一个新的答案并结合对原始答案的评论。然后由原始海报决定是否更新他的答案(以获取将来的赞誉)。谢谢你的链接!
Jeehut

嗯,好吧,这就是为什么我过去一直是忠实的杀手和跑步者的重要原因。我抓住我的答​​案,将其转换为当前语法,然后继续进行编码。我以这种方式发布的主要原因是,当我教Swift时,经常有人问我如何在最新的swift版本中找到解决方案,因为新的编码人员在更新其函数声明时遇到了麻烦。这是导致挫败感的主要原因之一,也是对比这两种代码样式的机会。结果,学生能够预测其他代码段中的类似变化。
James Larcombe '17

0

我需要完全覆盖超级视图。其他人在方向改变时不会这样做。因此,我编写了一个新的函数-使用20的任意大小乘数。随时更改您的需求。还要注意,实际上这会使子视图比父视图大很多,这可能与要求有所不同。

extension UIView {
    func coverSuperview() {
        guard let superview = self.superview else {
            assert(false, "Error! `superview` was nil – call `addSubview(_ view: UIView)` before calling `\(#function)` to fix this.")
            return
        }
        self.translatesAutoresizingMaskIntoConstraints = false
        let multiplier = CGFloat(20.0)
        NSLayoutConstraint.activate([
            self.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: multiplier),
            self.widthAnchor.constraint(equalTo: superview.widthAnchor, multiplier: multiplier),
            self.centerXAnchor.constraint(equalTo: superview.centerXAnchor),
            self.centerYAnchor.constraint(equalTo: superview.centerYAnchor),
            ])
    }
}

0

我从其他答案中挑选了最好的元素:

extension UIView {
  /// Adds constraints to this `UIView` instances `superview` object to make sure this always has the same size as the superview.
  /// Please note that this has no effect if its `superview` is `nil` – add this `UIView` instance as a subview before calling this.
  func bindFrameToSuperviewBounds() {
    guard let superview = self.superview else {
      print("Error! `superview` was nil – call `addSubview(view: UIView)` before calling `bindFrameToSuperviewBounds()` to fix this.")
      return
    }

    self.translatesAutoresizingMaskIntoConstraints = false

    NSLayoutConstraint.activate([
      self.topAnchor.constraint(equalTo: superview.topAnchor),
      self.bottomAnchor.constraint(equalTo: superview.bottomAnchor),
      self.leadingAnchor.constraint(equalTo: superview.leadingAnchor),
      self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
    ])
  }
}

您可以像这样在自定义UIView中使用它:

let myView = UIView()
myView.backgroundColor = UIColor.red

self.addSubview(myView)
myView.bindFrameToSuperviewBounds()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.