Answers:
试试这个 :
在目标C中
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
在斯威夫特
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
总是nil
如此,因此我将其更改为可选链windows[0]
并将其?
从可选链中删除,然后它起作用了。
要获得布局指南之间的高度,您只需执行
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
所以full frame height = 812.0
,safe area height = 734.0
下面是绿色视图的框架为 guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
,它确实具有安全框架。
斯威夫特4,5
使用约束将视图固定到安全区域锚可以在视图控制器生命周期的任何地方完成,因为约束是由API排队并在视图加载到内存后进行处理的。但是,获取安全区域值需要等到视图控制器生命周期的尽头,例如viewDidLayoutSubviews()
。
这可以插入任何视图控制器:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = view.safeAreaInsets.top
bottomSafeArea = view.safeAreaInsets.bottom
} else {
topSafeArea = topLayoutGuide.length
bottomSafeArea = bottomLayoutGuide.length
}
// safe area values are now available to use
}
我更喜欢这种方法(如果可能的话)使其脱离窗口,因为这是API的设计方式,更重要的是,在所有视图更改(例如设备方向更改)期间都会更新值。
但是,某些自定义呈现的视图控制器无法使用上述方法(我怀疑是因为它们处于瞬时容器视图中)。在这种情况下,您可以从根视图控制器获取值,该值将始终在当前视图控制器生命周期的任何位置可用。
anyLifecycleMethod()
guard let root = UIApplication.shared.keyWindow?.rootViewController else {
return
}
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = root.view.safeAreaInsets.top
bottomSafeArea = root.view.safeAreaInsets.bottom
} else {
topSafeArea = root.topLayoutGuide.length
bottomSafeArea = root.bottomLayoutGuide.length
}
// safe area values are now available to use
}
viewDidLayoutSubviews
(可以多次调用),这是一个即使在viewDidLoad
以下情况下也可以使用的解决方案:stackoverflow.com/a/53864017/7767664
这里没有其他答案对我有用,但是确实可以。
var topSafeAreaHeight: CGFloat = 0
var bottomSafeAreaHeight: CGFloat = 0
if #available(iOS 11.0, *) {
let window = UIApplication.shared.windows[0]
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
bottomSafeAreaHeight = window.frame.maxY - safeFrame.maxY
}
感谢所有提供帮助的人,这里的所有答案都很有帮助。
但是,正如我所看到的,安全区域主题有些混乱,似乎没有得到很好的记录。
因此,在这里我将尽可能概括它,以使其易于理解safeAreaInsets
,safeAreaLayoutGuide
并且LayoutGuide
。
在iOS 7中,Apple在中引入了topLayoutGuide
和bottomLayoutGuide
属性UIViewController
,它们允许您创建约束以防止内容被状态,导航或选项卡之类的UIKit栏隐藏。使用这些布局指南可以指定内容约束,避免出现约束被顶部或底部导航元素(UIKit栏,状态栏,导航或标签栏…)隐藏。
因此,例如,如果您想使tableView从顶部屏幕开始,您将执行以下操作:
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
在iOS 11中,Apple已弃用这些属性,并以一个安全区域布局指南代替了它们
根据Apple的安全区域
安全区域可帮助您将视图放置在整个界面的可见部分内。UIKit定义的视图控制器可以在内容顶部放置特殊视图。例如,导航控制器在基础视图控制器内容的顶部显示导航栏。即使这些视图是部分透明的,它们仍然会遮挡其下面的内容。在tvOS中,安全区域还包括屏幕的过扫描插图,这些插图代表屏幕边框所覆盖的区域。
下面是iPhone 8和iPhone X系列中突出显示的安全区域:
该safeAreaLayoutGuide
是一个属性UIView
达到以下高度safeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
这将返回图片中箭头的高度。
现在,如何获得顶部的“缺口”和底部的主屏幕指示器高度?
在这里我们将使用 safeAreaInsets
视图的安全区域反映了导航栏,选项卡栏,工具栏和其他使视图控制器的视图模糊的祖先所没有覆盖的区域。(在tvOS中,安全区域反映了屏幕边框未覆盖的区域。)通过将此属性中的插图应用于视图的边界矩形,可以获取视图的安全区域。如果该视图当前未安装在视图层次结构中,或者在屏幕上尚不可见,则此属性的边缘插图为0。
以下内容将显示iPhone 8和iPhone X系列之一上的不安全区域以及与边缘的距离。
现在,如果添加了导航栏
那么,现在如何获得不安全区域的高度?我们将使用safeAreaInset
这里是解决方案,但是它们在重要方面有所不同,
第一:
self.view.safeAreaInsets
这将返回EdgeInsets,您现在可以访问顶部和底部以了解插图,
第二个:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
您第一个正在获取视图插图,因此,如果有导航栏,它将被考虑,但是第二个您正在访问窗口的safeAreaInsets,因此将不考虑导航栏
Swift 5,Xcode 11.4
`UIApplication.shared.keyWindow`
它将发出弃用警告。在iOS 13.0中已弃用了“ keyWindow”:不应用于支持多个场景的应用程序,因为它会由于连接的场景而在所有连接的场景之间返回一个关键窗口。我用这种方式。
extension UIView {
var safeAreaBottom: CGFloat {
if #available(iOS 11, *) {
if let window = UIApplication.shared.keyWindowInConnectedScenes {
return window.safeAreaInsets.bottom
}
}
return 0
}
var safeAreaTop: CGFloat {
if #available(iOS 11, *) {
if let window = UIApplication.shared.keyWindowInConnectedScenes {
return window.safeAreaInsets.top
}
}
return 0
}
}
extension UIApplication {
var keyWindowInConnectedScenes: UIWindow? {
return windows.first(where: { $0.isKeyWindow })
}
}
Swift 5扩展
可以用作扩展,并可以通过以下方式调用:UIApplication.topSafeAreaHeight
extension UIApplication {
static var topSafeAreaHeight: CGFloat {
var topSafeAreaHeight: CGFloat = 0
if #available(iOS 11.0, *) {
let window = UIApplication.shared.windows[0]
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
}
return topSafeAreaHeight
}
}
UIApplication的扩展是可选的,可以是UIView的扩展或任何首选的扩展,甚至可以是更好的全局函数。
更全面的方法
import SnapKit
let containerView = UIView()
containerView.backgroundColor = .red
self.view.addSubview(containerView)
containerView.snp.remakeConstraints { (make) -> Void in
make.width.top.equalToSuperView()
make.top.equalTo(self.view.safeArea.top)
make.bottom.equalTo(self.view.safeArea.bottom)
}
extension UIView {
var safeArea: ConstraintBasicAttributesDSL {
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.snp
}
return self.snp
}
var isIphoneX: Bool {
if #available(iOS 11.0, *) {
if topSafeAreaInset > CGFloat(0) {
return true
} else {
return false
}
} else {
return false
}
}
var topSafeAreaInset: CGFloat {
let window = UIApplication.shared.keyWindow
var topPadding: CGFloat = 0
if #available(iOS 11.0, *) {
topPadding = window?.safeAreaInsets.top ?? 0
}
return topPadding
}
var bottomSafeAreaInset: CGFloat {
let window = UIApplication.shared.keyWindow
var bottomPadding: CGFloat = 0
if #available(iOS 11.0, *) {
bottomPadding = window?.safeAreaInsets.bottom ?? 0
}
return bottomPadding
}
}