【问题标题】:Width and Height Equal to its superView using autolayout programmatically?宽度和高度等于它的superView以编程方式使用自动布局?
【发布时间】:2013-09-16 09:21:12
【问题描述】:

我一直在网上寻找很多sn-ps,但我仍然找不到我的问题的答案。我的问题是我有一个scrollView(SV),我想以编程方式在scrollView(SV)中添加一个按钮,其超视图的宽度和高度相同,即scrollView(SV),这样当用户旋转设备按钮时,它将具有相同的框架滚动视图(SV)。如何做 NSLayout/NSLayoutConstraint?谢谢

【问题讨论】:

    标签: ios swift dynamic autolayout nslayoutconstraint


    【解决方案1】:

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

    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()
    

    【讨论】:

      【解决方案2】:

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

      斯威夫特 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]))
          }
      }
      

      斯威夫特 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) 之后的 'required init?(coder aDecoder)' 内调用 bindFrameToSuperviewBounds
      • 值得注意的是,使用视觉格式的解决方案对安全区域不友好。例如,如果您在显示导航栏和工具栏的导航控制器内的视图中调用此方法,则您的视图将位于导航栏下方,如果向下那么远,您的视图将位于工具栏下方。
      • 这也可以作为 Swift 5 的解决方案。我无法使用 AutoLayout 使我的自定义 subView 适应 parentView 大小。在添加子视图后使用它就像一个魅力。
      • Swift 4.2 解决方案运行良好。您甚至可以通过删除 constant: 0 部分使其更短。
      【解决方案3】:

      我需要完全覆盖超级视图。其他的在方向变化时不会这样做。所以我写了一个新的——使用任意大小的乘数 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),
                  ])
          }
      }
      

      【讨论】:

        【解决方案4】:

        方法一:通过 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。它使处理约束更容易并且更不容易出错

        按照文档中的 installation instructions 将 SnapKit 包含到您的项目中。然后 import 在你的 Swift 文件的顶部:

        import SnapKit
        

        现在你可以用这个来达到同样的效果:

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

        【讨论】:

          【解决方案5】:

          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])
          

          【讨论】:

            【解决方案6】:

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

            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 部分使其更短。
            【解决方案7】:

            作为@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]))
            }
            

            【讨论】:

            • 如果您只更改代码以使其符合 Swift 3 标准,您应该编辑原始发帖人的答案,而不是发布新答案(因为这并没有改变原始发帖人的意图)。如果您没有足够的积分进行编辑,请在原始帖子上发表评论,并提示遵守 Swift 3 所需的更改。原始发帖人(或看到您评论的其他人)可能会更新答案。这样我们就可以保持线程干净,避免重复答案和不推荐使用的代码。
            • 我完全同意你的观点,@Dschee,但是(对我来说,荒谬的)Meta 评论说“我们不会在 SO 上这样做”......meta.stackoverflow.com/questions/339024/…
            • @JoeBlow 在阅读了您链接背后的讨论之后,我实际上认为这也是有道理的。我同意 PatrickHaugh 的评论(关于这个问题),尽管应该给出一个新的答案以及对原始答案的评论。然后由原始发布者来更新他的答案(以获得未来的支持)或不更新。谢谢你的链接!
            • 嗯,好吧,这就是为什么我过去一直是忠实的击球手和跑步者的重要原因。我抓住我的答​​案,将其转换为当前语法并继续我的编码。我以这种方式发布的主要原因是,当我教 swift 时,我经常被问到如何在任何当前版本的 swift 中找到解决方案,因为新的编码人员在更新他们的函数声明时遇到了麻烦。这是令人沮丧的主要来源之一,但也是对比两种代码风格的机会。结果,学生能够预测其他代码 sn-ps 中的类似变化。
            【解决方案8】:

            斯威夫特 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 所需的更改。原始发帖人(或看到您评论的其他人)可能会更新答案。这样我们就可以保持线程干净,避免重复答案和不推荐使用的代码。
            • 嘿@Dschee - 我完全同意你的看法,但我们“错了”。网站上的“共识”观点是好是坏,与您在此处表达的相反。 meta.stackoverflow.com/questions/339024/…(我一直无视共识决定,做明智的事,然后从模组中惹麻烦:))
            【解决方案9】:

            作为补充答案,对于那些不反对包含第三方库的人来说,PureLayout 库提供了一种方法来做到这一点。 安装库后,就很简单了

            myView.autoPinEdgesToSuperviewEdges()
            

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

            【讨论】:

              【解决方案10】:

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

              编辑:

              使用以下代码 sn-p,其中 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)]];
              

              【讨论】:

              • 在这种情况下,视觉格式也可以是V:|[subview]|H:|[subview]|
              • 值得注意的是(多年后)这段代码已经过时了。现在添加约束要容易得多 - 请参阅下面的任何 2017 年答案
              【解决方案11】:

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

              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];
              

              【讨论】:

              • 使用NSLayoutConstraint.activateConstraints([width, height, top, leading])会效率更高
              • 你可以使用[coverForScrolView addConstraints:@[width, height, top, leading]];
              • 值得注意的是(多年后)这段代码已经过时了。现在添加约束要容易得多 - 请参阅下面的任何 2017 年答案
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多