如何以编程方式使用“安全区域布局”?


100

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

我试图将我的观点锚定到

view.safeAreaLayoutGuide

但它们始终与iPhone X模拟器中的顶级产品重叠。


1
根据以下文档,没有记录的bool属性:developer.apple.com/documentation/uikit/uiview/…–
dvp.petrov

怎么view.safeAreaInsets样 你有尝试过吗?
Karthikeyan Bose

@KarthikeyanBose是的,我很不幸没有运气。
菲利普(Phillip)

为我工作。代码是什么样的
Daniel Springer

Answers:


157

这是示例代码(请参阅:安全区域布局指南):
请参阅如果在代码中创建约束,请使用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)
   ])
}

结果:

在此处输入图片说明

在此处输入图片说明


这是Apple Developer 安全区域布局指南的官方文档


需要安全区域来处理iPhone-X的用户界面设计。这是有关如何使用“安全区域布局”为iPhone-X设计用户界面的基本指南


2
是否也可以在目标C中给出呢?它看起来像正是我需要的
汤姆·哈蒙德

6
@TomHammond这是在Objective-C中为您提供stackoverflow.com/a/47076040/5638630
Krunal

2
@ZonilyJame现在关于您的查询-SaFeAreaLayout是iOS特定的框架(不是特定于设备iPhoneX的),它替代了iOS 11中的“上下布局”指南,因此我们必须为iOS使用/设置条件,而不是为设备使用。SafeAreaLayout负责所有类型设备(iPhone-X和其他设备)的设计。如果您仍然有任何疑问/困惑,可以向我询问更多详细信息。
克鲁纳尔

3
太棒了 谢谢您:)
Rajamohan S

1
为什么它被接受为正确答案?我试图设置一个具体的位置-结果是完全随机的-控件被放置在无法预测的位置!
Vyachaslav Gerchicov,

83

我实际上是为它使用扩展,并控制它是否为ios 11。

extension UIView {

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

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

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

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

这是一种简单的方法,可以解决问题。谢谢你的主意。
alper_k

这是一个简单而又不错的方法!请注意使用self.safeAreaLayoutGuide代替self.layoutMarginsGuide。此答案中使用的安全保险箱可以让我正确地呆在安全区内!我建议更改的一件事是使用leadingAnchortrailingAnchor而不是leftAnchorrightAnchor。太棒了!
Pranoy C,

22

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


2
viewDidAppear除非您完全知道自己在做什么,否则永远不要在中进行设置约束。viewDidAppear被多次调用,因此,每次调用时,约束都会重复。
Yevhen Dubinin

是! ,经过编辑的答案,可以用作您的用例。
杰克

14

对于像我一样使用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()
     }
}

1
好答案。为了使它更紧凑,您可以说:if #available(iOS 11.0,*){make.edges.equalTo(view.safeAreaLayoutGuide.snp.margins)}
Don Miguel

7

我正在使用此方法,而不是在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的一个简单示例。
托尼·特兰

6

使用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]

3
如果您的视图未使用自动版式,则这是一种方法。
乔纳森·卡布雷拉

4

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

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

结果


2
拒绝投票时请提供评论,以便我们所有人都可以了解该技术是否不适用。谢谢!
AtomicBoolean

这行得通,我也喜欢视觉格式解决方案!谢谢!但这适用于所有ios版本吗?
PaFi

4

适用于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)。结果忽略状态栏
Vyachaslav Gerchicov

2

Swift 4.2和5.0。假设您想在viewBg上添加Leading,Trailing,Top和Bottom约束。因此,您可以使用以下代码。

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

1

此扩展帮助您将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
    }

}

0

您可以按照以下说明使用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-|
  """
...
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.