Câu trả lời:
Thử cái này :
Trong mục tiêu C
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
Trong Swift
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
luôn luôn là nil
vì vậy tôi đã thay đổi nó windows[0]
và loại bỏ ?
khỏi chuỗi tùy chọn, sau đó nó hoạt động.
Để có được chiều cao giữa các hướng dẫn bố trí bạn chỉ cần làm
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
Vì vậy full frame height = 812.0
,safe area height = 734.0
Dưới đây là ví dụ trong đó chế độ xem màu xanh lá cây có khung guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
, trong đó có khung an toàn.
Swift 4, 5
Để ghim chế độ xem vào neo khu vực an toàn bằng cách sử dụng các ràng buộc có thể được thực hiện ở bất kỳ đâu trong vòng đời của bộ điều khiển chế độ xem vì chúng được xếp hàng bởi API và được xử lý sau khi chế độ xem được tải vào bộ nhớ. Tuy nhiên, nhận được các giá trị của vùng an toàn đòi hỏi phải đợi đến hết vòng đời của bộ điều khiển xem, như thế nào viewDidLayoutSubviews()
.
Điều này cắm vào bất kỳ trình điều khiển xem:
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
}
Tôi thích phương pháp này để đưa nó ra khỏi cửa sổ (khi có thể) bởi vì đó là cách API được thiết kế và quan trọng hơn là các giá trị được cập nhật trong tất cả các thay đổi về chế độ xem, như thay đổi hướng thiết bị.
Tuy nhiên, một số bộ điều khiển khung nhìn được trình bày tùy chỉnh không thể sử dụng phương pháp trên (tôi nghi ngờ vì chúng ở dạng xem container tạm thời). Trong các trường hợp như vậy, bạn có thể lấy các giá trị ra khỏi trình điều khiển chế độ xem gốc, sẽ luôn có sẵn ở bất kỳ đâu trong vòng đời của trình điều khiển chế độ xem hiện tại.
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
(có thể được gọi nhiều lần), đây là một giải pháp sẽ hoạt động ngay cả trong viewDidLoad
: stackoverflow.com/a/53864017/7767664
Không có câu trả lời nào khác ở đây làm việc cho tôi, nhưng điều này đã làm.
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
}
Tất cả các câu trả lời ở đây đều hữu ích, Cảm ơn mọi người đã đề nghị giúp đỡ.
Tuy nhiên, như tôi thấy rằng chủ đề về khu vực an toàn có một chút nhầm lẫn sẽ không xuất hiện để được ghi chép lại.
Vì vậy, tôi sẽ tóm tắt nó ở đây càng tốt càng tốt để làm cho nó dễ hiểu safeAreaInsets
, safeAreaLayoutGuide
và LayoutGuide
.
Trong iOS 7, Apple đã giới thiệu topLayoutGuide
và bottomLayoutGuide
các thuộc tính trong UIViewController
. Họ cho phép bạn tạo các ràng buộc để giữ cho nội dung của bạn không bị ẩn bởi các thanh UIKit như trạng thái, điều hướng hoặc thanh tab Có thể với các hướng dẫn bố cục này để chỉ định các ràng buộc về nội dung, tránh nó được ẩn bởi các yếu tố điều hướng trên cùng hoặc dưới cùng (thanh UIKit, thanh trạng thái, thanh điều hướng hoặc thanh tab).
Vì vậy, ví dụ nếu bạn muốn tạo một bảng Xem bắt đầu từ màn hình trên cùng, bạn đã làm một cái gì đó như thế:
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
Trong iOS 11, Apple đã từ chối các thuộc tính này thay thế chúng bằng một hướng dẫn bố cục vùng an toàn duy nhất
Khu vực an toàn theo Apple
Các khu vực an toàn giúp bạn đặt chế độ xem của mình trong phần hiển thị của giao diện tổng thể. Bộ điều khiển chế độ xem do UIKit xác định có thể đặt các chế độ xem đặc biệt lên trên nội dung của bạn. Ví dụ: bộ điều khiển điều hướng hiển thị một thanh điều hướng bên trên nội dung của bộ điều khiển xem bên dưới. Ngay cả khi các chế độ xem như vậy được minh bạch một phần, chúng vẫn ẩn chứa nội dung bên dưới chúng. Trong tvOS, khu vực an toàn cũng bao gồm các miếng đệm quá mức của màn hình, đại diện cho khu vực được bao phủ bởi viền màn hình.
Dưới đây, một khu vực an toàn được tô sáng trong iPhone 8 và iPhone X-series:
Đây safeAreaLayoutGuide
là một tài sản củaUIView
Để có được chiều cao của safeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
Điều đó sẽ trả về chiều cao của Mũi tên trong ảnh của bạn.
Bây giờ, những gì về việc có được chiều cao chỉ báo màn hình chính "đỉnh cao" và dưới cùng?
Ở đây chúng tôi sẽ sử dụng safeAreaInsets
Vùng an toàn của chế độ xem phản ánh khu vực không được bao phủ bởi các thanh điều hướng, thanh tab, thanh công cụ và các tổ tiên khác che khuất tầm nhìn của bộ điều khiển chế độ xem. (Trong tvOS, khu vực an toàn phản ánh khu vực không được bao phủ bởi viền màn hình.) Bạn có được khu vực an toàn để xem bằng cách áp dụng các phần tử trong thuộc tính này vào hình chữ nhật giới hạn của chế độ xem. Nếu chế độ xem hiện chưa được cài đặt trong cấu trúc phân cấp chế độ xem hoặc chưa hiển thị trên màn hình, thì các cạnh trong thuộc tính này là 0.
Dưới đây sẽ hiển thị khu vực không an toàn và có khoảng cách từ các cạnh trên iPhone 8 và một trong iPhone X-Series.
Bây giờ, nếu thanh điều hướng được thêm vào
Vì vậy, bây giờ làm thế nào để có được chiều cao khu vực không an toàn? chúng tôi sẽ sử dụngsafeAreaInset
Đây là giải pháp tuy nhiên chúng khác nhau ở một điều quan trọng,
Đầu tiên:
self.view.safeAreaInsets
Điều đó sẽ trả về EdgeInsets, bây giờ bạn có thể truy cập vào đầu và cuối để biết các phần tử,
Cái thứ hai:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
Cái đầu tiên bạn đang dùng trong phần xem, vì vậy nếu có thanh điều hướng thì nó sẽ được xem xét, tuy nhiên cái thứ hai bạn đang truy cập safeAreaInsets của cửa sổ để thanh điều hướng sẽ không được xem xét
safeAreaLayoutGuide Khi màn hình hiển thị trên màn hình, hướng dẫn này phản ánh phần của chế độ xem không được bao phủ bởi các thanh điều hướng, thanh tab, thanh công cụ và các chế độ xem tổ tiên khác. (Trong tvOS, vùng an toàn phản ánh vùng không được che chắn của màn hình.) Nếu chế độ xem hiện chưa được cài đặt trong cấu trúc phân cấp chế độ xem hoặc chưa hiển thị trên màn hình, các cạnh của hướng dẫn bố cục bằng với các cạnh của chế độ xem.
Sau đó, để có được chiều cao của mũi tên màu đỏ trong ảnh chụp màn hình, đó là:
self.safeAreaLayoutGuide.layoutFrame.size.height
Swift 5, Xcode 11.4
`UIApplication.shared.keyWindow`
Nó sẽ đưa ra cảnh báo khấu hao. '' keyWindow 'không được dùng trong iOS 13.0: Không nên sử dụng cho các ứng dụng hỗ trợ nhiều cảnh vì nó trả về một cửa sổ chính trên tất cả các cảnh được kết nối' vì các cảnh được kết nối. Tôi sử dụng cách này.
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 })
}
}
Mục tiêu-C Ai gặp vấn đề khi keyWindow bằng nil . Chỉ cần đặt mã ở trên trong viewDidAppear (không phải trong viewDidLoad)
Tiện ích mở rộng Swift 5
Điều này có thể được sử dụng như một phần mở rộng và được gọi với: 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
}
}
Mở rộng UIApplication là tùy chọn, có thể là một phần mở rộng của UIView hoặc bất cứ thứ gì được ưa thích, hoặc thậm chí có thể tốt hơn một chức năng toàn cầu.
Một cách tiếp cận tròn hơn
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
}
}