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

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

我试图把我的观点

view.safeAreaLayoutGuide

但它们一直重叠在 iPhone X 模拟器的顶级水平上。

174068 次浏览

下面是示例代码(参考自: 安全区布局指南) :
如果在代码中创建约束,请使用 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或更高版本,否则你需要把安全区域布局指南约束用 # 可用包装起来,然后回到早期 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)
])
}

结果:

enter image description here

enter image description here


以下是苹果开发者的官方文档: 安全区域布局指南


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

我没有在 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]
]];

也请检查选项较低版本的 ios 11从 Krunal 的答案。

SafeAreaLayoutGuideUIView的属性,

SafeAreaLayoutGuide 的顶部指示未遮盖的顶部边缘 视野(例如不是背后) 状态栏或导航栏,如果存在)。类似的其他边缘。

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

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

肖像 + 景观的约束是-

Portrait image

Landscape image

        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

SafeArea 布局指南

对于那些使用 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()
}
}

我实际上使用了一个扩展来控制它是不是 iOS11。

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

使用可视化格式的约束,你就可以免费获得对安全区域的尊重。

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

result

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

您可以使用这里解释的 视图 safeAreaInsets

代码示例(取自 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-|
"""
...

目标 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和5.0。假设您想在 视图上添加 引导跟踪头儿波顿约束。因此,您可以使用下面的代码。

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

这个扩展可以帮助你将 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
}


}
var someVeiw = UIView()
func webView(_view: View, didFinish navigation: WKNavigation!) {
self.someVeiw.frame = view.safeAreaLayoutGuide.layoutFrame
}

IOS10 + 附带了一个 safeAreaLayoutGuide,可以在运行时检测到。Frame on 接受 CGRect,因此使用 layoutFrame