【问题标题】:How do I use Safe Area Layout programmatically?如何以编程方式使用安全区域布局?
【发布时间】:2018-03-01 05:16:59
【问题描述】:

由于我不使用故事板来创建我的视图,我想知道是否有“使用安全区域指南”选项以编程方式或类似的方式。

我试图将我的观点锚定到

view.safeAreaLayoutGuide

但它们在 iPhone X 模拟器中不断重叠。

【问题讨论】:

标签: ios swift iphone-x safearealayoutguide


【解决方案1】:

我实际上正在使用它的扩展程序并控制它是否是 iOS 11。

extension UIView {

  var safeTopAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return safeAreaLayoutGuide.topAnchor
    }
    return topAnchor
  }

  var safeLeftAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *){
      return safeAreaLayoutGuide.leftAnchor
    }
    return leftAnchor
  }

  var safeRightAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *){
      return safeAreaLayoutGuide.rightAnchor
    }
    return rightAnchor
  }

  var safeBottomAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return safeAreaLayoutGuide.bottomAnchor
    }
    return bottomAnchor
  }
}

【讨论】:

  • 这是一种非常简单的方法,并且成功了。谢谢你的想法。
  • 这是一个非常简单但很好的方法!注意使用self.safeAreaLayoutGuide 而不是self.layoutMarginsGuide。此答案中使用的保险箱可以正常工作,让我留在安全区域内!我建议改变的一件事是使用leadingAnchortrailingAnchor 而不是leftAnchorrightAnchor。太棒了!
【解决方案2】:

这个扩展可以帮助你将 UIVIew 约束到它的 superview 和 superview+safeArea:

extension UIView {

    ///Constraints a view to its superview
    func constraintToSuperView() {
        guard let superview = superview else { return }
        translatesAutoresizingMaskIntoConstraints = false

        topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
        leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true
        bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
        rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true
    }

    ///Constraints a view to its superview safe area
    func constraintToSafeArea() {
        guard let superview = superview else { return }
        translatesAutoresizingMaskIntoConstraints = false

        topAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.topAnchor).isActive = true
        leftAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.leftAnchor).isActive = true
        bottomAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.bottomAnchor).isActive = true
        rightAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.rightAnchor).isActive = true
    }

}

【讨论】:

    【解决方案3】:

    这里是示例代码(参考来自:Safe Area Layout Guide):
    如果您在代码中创建约束,请使用 UIView 的 safeAreaLayoutGuide 属性来获取相关的布局锚点。让我们在代码中重新创建上面的 Interface Builder 示例,看看它的外观:

    假设我们的视图控制器中有绿色视图作为属性:

    private let greenView = UIView()
    

    我们可能有一个函数来设置从 viewDidLoad 调用的视图和约束:

    private func setupView() {
      greenView.translatesAutoresizingMaskIntoConstraints = false
      greenView.backgroundColor = .green
      view.addSubview(greenView)
    }
    

    像往常一样使用根视图的 layoutMarginsGuide 创建前导和尾随边距约束:

     let margins = view.layoutMarginsGuide
     NSLayoutConstraint.activate([
        greenView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
        greenView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
     ])
    

    现在,除非您的目标是 iOS 11 及更高版本,否则您需要使用 #available 包装安全区域布局指南约束,并回退到早期 iOS 版本的顶部和底部布局指南:

    if #available(iOS 11, *) {
      let guide = view.safeAreaLayoutGuide
      NSLayoutConstraint.activate([
       greenView.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0),
       guide.bottomAnchor.constraintEqualToSystemSpacingBelow(greenView.bottomAnchor, multiplier: 1.0)
       ])
    } else {
       let standardSpacing: CGFloat = 8.0
       NSLayoutConstraint.activate([
       greenView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: standardSpacing),
       bottomLayoutGuide.topAnchor.constraint(equalTo: greenView.bottomAnchor, constant: standardSpacing)
       ])
    }
    

    结果:


    这是Safe Area Layout Guide的Apple Developer官方文档


    需要安全区域来处理 iPhone-X 的用户界面设计。这是How to design user interface for iPhone-X using Safe Area Layout的基本指南

    【讨论】:

    • 是否也可以在 Objective-C 中给出这个?看起来正是我所需要的
    • @TomHammond 这是 Objective-C 为你准备的 stackoverflow.com/a/47076040/5638630
    • @ZonilyJame 现在关于您的查询 - SaFeAreaLayout 是 iOS 特定框架(不是设备 iPhoneX 特定),它正在取代 iOS 11 中的上下布局指南,因此我们必须使用/设置 iOS 而非设备的条件. SafeAreaLayout 负责所有类型设备(iPhone-X 和其他设备)的设计。如果您仍有任何疑问/困惑,可以向我询问更多详细信息。
    • 太棒了。谢谢你:)
    • 为什么它被接受为正确答案?我试图设置一个具体的位置 - 结果是完全随机的 - 控件被放置在不可预测的位置!
    【解决方案4】:

    Swift 4.2 和 5.0。假设您要在 viewBg 上添加前导、尾随、顶部和底部约束。所以,你可以使用下面的代码。

    let guide = self.view.safeAreaLayoutGuide
    viewBg.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
    viewBg.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
    viewBg.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
    viewBg.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
    

    【讨论】:

      【解决方案5】:

      Objective-C 的安全区域扩展

      @implementation UIView (SafeArea)
      
      - (NSLayoutAnchor *)safeTopAnchor{
      
          if (@available(iOS 11.0, *)){
              return self.safeAreaLayoutGuide.topAnchor;
          } else {
              return self.topAnchor;
          }
      
      }
      
      
      - (NSLayoutAnchor *)safeBottomAnchor{
      
          if (@available(iOS 11.0, *)) {
              return self.safeAreaLayoutGuide.bottomAnchor;
          } else {
              return self.bottomAnchor;
          }
      
      }
      
      @end
      

      【讨论】:

      • 它不起作用(Swift 4.2,iOS 12)。结果忽略状态栏
      【解决方案6】:

      您可以使用 view.safeAreaInsets,如此处https://www.raywenderlich.com/174078/auto-layout-visual-format-language-tutorial-2

      所述

      代码示例(取自 raywenderlich.com):

      override func viewSafeAreaInsetsDidChange() {
        super.viewSafeAreaInsetsDidChange()
      
        if !allConstraints.isEmpty {
          NSLayoutConstraint.deactivate(allConstraints)
          allConstraints.removeAll()
        }
      
        let newInsets = view.safeAreaInsets
        let leftMargin = newInsets.left > 0 ? newInsets.left : Metrics.padding
        let rightMargin = newInsets.right > 0 ? newInsets.right : Metrics.padding
        let topMargin = newInsets.top > 0 ? newInsets.top : Metrics.padding
        let bottomMargin = newInsets.bottom > 0 ? newInsets.bottom : Metrics.padding
      
        let metrics = [
          "horizontalPadding": Metrics.padding,
          "iconImageViewWidth": Metrics.iconImageViewWidth,
          "topMargin": topMargin,
          "bottomMargin": bottomMargin,
          "leftMargin": leftMargin,
          "rightMargin": rightMargin]
      }
      
      
      let views: [String: Any] = [
        "iconImageView": iconImageView,
        "appNameLabel": appNameLabel,
        "skipButton": skipButton,
        "appImageView": appImageView,
        "welcomeLabel": welcomeLabel,
        "summaryLabel": summaryLabel,
        "pageControl": pageControl]
      
      let iconVerticalConstraints = NSLayoutConstraint.constraints(
        withVisualFormat: "V:|-topMargin-[iconImageView(30)]",
        metrics: metrics,
        views: views)
      allConstraints += iconVerticalConstraints
      
      let topRowHorizontalFormat = """
        H:|-leftMargin-[iconImageView(iconImageViewWidth)]-[appNameLabel]-[skipButton]-rightMargin-|
        """
      ...
      

      【讨论】:

        【解决方案7】:

        使用UIWindowUIViewsafeAreaInsets.bottom.top.left.right

        // #available(iOS 11.0, *)
        // height - UIApplication.shared.keyWindow!.safeAreaInsets.bottom
        
        // On iPhoneX
        // UIApplication.shared.keyWindow!.safeAreaInsets.top =  44
        // UIApplication.shared.keyWindow!.safeAreaInsets.bottom = 34
        
        // Other devices
        // UIApplication.shared.keyWindow!.safeAreaInsets.top =  0
        // UIApplication.shared.keyWindow!.safeAreaInsets.bottom = 0
        
        // example
        let window = UIApplication.shared.keyWindow!
        let viewWidth = window.frame.size.width
        let viewHeight = window.frame.size.height - window.safeAreaInsets.bottom
        let viewFrame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight)
        let aView = UIView(frame: viewFrame)
        aView.backgroundColor = .red
        view.addSubview(aView)
        aView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        

        【讨论】:

        • 如果您的视图没有使用 AutoLayout,这就是要走的路。
        【解决方案8】:

        使用具有视觉格式的约束,您可以免费获得对安全区域的尊重。

        class ViewController: UIViewController {
        
            var greenView = UIView()
        
            override func viewDidLoad() {
                super.viewDidLoad()
                greenView.backgroundColor = .green
                view.addSubview(greenView)
            }
            override func viewWillLayoutSubviews() {
                super.viewWillLayoutSubviews()
        
                greenView.translatesAutoresizingMaskIntoConstraints = false
                let views : [String:Any] = ["greenView":greenView]
                view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[greenView]-|", options: [], metrics: nil, views: views))
                view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[greenView]-|", options: [], metrics: nil, views: views))
            }
        }
        

        【讨论】:

        • 请在投反对票时提供评论,以便我们都可以了解是否存在这种技术不适用的情况。谢谢!
        • 这个可行,我也喜欢视觉格式解决方案!谢谢!但这适用于所有 ios 版本吗?
        【解决方案9】:

        SafeAreaLayoutGuideUIView 属性,

        safeAreaLayoutGuide 的顶部表示未被遮挡的顶部边缘 视图(例如,不在后面 状态栏或导航栏(如果存在)。其他边缘也是如此。

        使用safeAreaLayoutGuide 避免我们的对象从圆角、导航栏、标签栏、工具栏和其他祖先视图中剪切/重叠。

        我们可以分别创建safeAreaLayoutGuide对象和设置对象约束。

        纵向 + 横向的约束是 -

                self.edgesForExtendedLayout = []//Optional our as per your view ladder
        
                let newView = UIView()
                newView.backgroundColor = .red
                self.view.addSubview(newView)
                newView.translatesAutoresizingMaskIntoConstraints = false
                if #available(iOS 11.0, *) {
                    let guide = self.view.safeAreaLayoutGuide
                    newView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
                    newView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
                    newView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
                    newView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        
                }
                else {
                    NSLayoutConstraint(item: newView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
                    NSLayoutConstraint(item: newView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
                    NSLayoutConstraint(item: newView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
        
                    newView.heightAnchor.constraint(equalToConstant: 100).isActive = true
                }
        

        UILayoutGuide

        safeAreaLayoutGuide

        【讨论】:

        • 永远不要在viewDidAppear 中设置约束,除非您完全知道自己在做什么。 viewDidAppear 被多次调用,因此每次调用时都会复制您的约束。
        • 是的! , 编辑后的答案,您可以用作您的用例。
        【解决方案10】:

        对于像我一样使用SnapKit 的人来说,解决方案是将您的约束锚定到view.safeAreaLayoutGuide,如下所示:

        yourView.snp.makeConstraints { (make) in
            if #available(iOS 11.0, *) {
                //Bottom guide
                make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottomMargin)
                //Top guide
                make.top.equalTo(view.safeAreaLayoutGuide.snp.topMargin)
                //Leading guide
                make.leading.equalTo(view.safeAreaLayoutGuide.snp.leadingMargin)
                //Trailing guide
                make.trailing.equalTo(view.safeAreaLayoutGuide.snp.trailingMargin)
        
             } else {
                make.edges.equalToSuperview()
             }
        }
        

        【讨论】:

        • 很好的答案。为了使它更紧凑,你可以说: if #available(iOS 11.0, *) { make.edges.equalTo(view.safeAreaLayoutGuide.snp.margins) }
        【解决方案11】:

        我使用它而不是向 layoutMarginsGuide 添加前导和尾随边距约束:

        UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
        yourView.translatesAutoresizingMaskIntoConstraints = NO;
        [NSLayoutConstraint activateConstraints:@[
                                                   [safe.trailingAnchor constraintEqualToAnchor:yourView.trailingAnchor],
                                                   [yourView.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor],
                                                   [yourView.topAnchor constraintEqualToAnchor:safe.topAnchor],
                                                   [safe.bottomAnchor constraintEqualToAnchor:yourView.bottomAnchor]
                                                  ]];
        

        还请从 Krunal 的回答中检查低版本 ios 11 的选项。

        【讨论】:

        • 确保您已经将 yourView 添加到 superView。就是 self.view 在我的代码中作为一个简单的例子。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-05-26
        • 2018-05-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多