Nhận khu vực an toàn chèn chiều cao trên và dưới


177

Trên iPhone X mới, điều gì sẽ là cách thích hợp nhất để có được cả chiều cao trên và dưới cho các khu vực không an toàn?

nhập mô tả hình ảnh ở đây

Câu trả lời:


364

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
}

1
@KrutikaSonawala bottomPadding + 10 (giá trị của bạn)
user6788419

12
Điều này dường như không hoạt động đối với tôi - in ra giá trị của UIApplication. Shared.keyWindow? .SafeAreaInsets trả về tất cả 0 giây. Có ý kiến ​​gì không?
Hai

2
Tôi đã làm, tôi có thể chống lại safeAreaLayoutGuides của bất kỳ chế độ xem nào nhưng trực tiếp in ra giá trị của safeAreaInsets của cửa sổ chính chỉ trả về số 0. Đây là vấn đề vì tôi cần phải hạn chế một cách thủ công một chế độ xem đã được thêm vào cửa sổ chính nhưng không nằm trong chế độ xem của trình điều khiển chế độ xem gốc.
Hải

5
Đối với tôi, keyWindowluôn luôn là nilvì 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.
George_E

2
Nó chỉ hoạt động khi chế độ xem của bộ điều khiển đã xuất hiện.
Vanya

84

Để 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

nhập mô tả hình ảnh ở đây


Cảm ơn đó là sự dẫn dắt mà tôi cũng đã nghĩ đến. Tôi không nghĩ có một giải pháp đáng tin cậy hơn. Nhưng với điều này tôi chỉ có được chiều cao không an toàn đầy đủ, phải không? Không phải hai chiều cao cho mỗi khu vực?
Tulleb

2
Khi các quan điểm đã được đưa ra, điều này sẽ cho bạn câu trả lời chính xác
Ladislav

2
Trên thực tế, câu trả lời của @ user6788419 cũng rất đẹp và ngắn hơn.
Tulleb

Tôi đang nhận được chiều cao 812.0 với mã đó, sử dụng chế độ xem của bộ điều khiển. Vì vậy, tôi đang sử dụng UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame, trong đó có khung an toàn.
Ferran Maylinch

1
@FerranMaylinch Không chắc chắn nơi bạn đang kiểm tra chiều cao, nhưng tôi cũng có 812, hóa ra là do kiểm tra giá trị trước khi chế độ xem hiển thị. Trong trường hợp đó, độ cao sẽ giống nhau "Nếu chế độ xem hiện chưa được cài đặt trong 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" developer.apple.com/documentation/ uikit / uiview / Bắn
Nicholas Harlen

80

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
}

3
Vui lòng khai báo bằng cách sử dụng, hãy để topSafeArea: CGFloat thay thế!
Gusutafu

tránh sử dụng 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
user924

@ user924 thì các giá trị sẽ không được cập nhật khi vùng an toàn thay đổi (nghĩa là thanh trạng thái hai chiều cao)
bsod

@bsod thì điều này sẽ tốt hơn stackoverflow.com/a 43220550/7767664 (vì nó chỉ dành cho thanh trạng thái chứ không phải các chế độ xem khác)
user924

Hoặc thay vì đi qua đại biểu ứng dụng, bạn chỉ có thể sử dụng API khu vực an toàn mà Apple tích hợp trong bộ điều khiển xem.
bsod

21

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
  }

1
Mã của bạn sẽ hoạt động trong bất kỳ phần nào của vòng đời của bộ điều khiển xem. Nếu bạn muốn sử dụng view.safeAreaInsets.top, thì bạn cần đảm bảo rằng bạn gọi nó sau trong viewDidAppear hoặc sau này. Trong viewDidLoad, bạn sẽ không nhận được view.safeAreaInsets.top với giá trị chính xác vì nó chưa được khởi tạo.
dùng3204765

1
câu trả lời hay nhất từ ​​trước đến nay
Rikco

17

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, safeAreaLayoutGuideLayoutGuide.

Trong iOS 7, Apple đã giới thiệu topLayoutGuidebottomLayoutGuidecá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:

nhập mô tả hình ảnh ở đây

Đây safeAreaLayoutGuidelà 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.

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Bây giờ, nếu thanh điều hướng được thêm vào

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

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


5

Trong iOS 11, có một phương thức cho biết khi safeArea thay đổi.

override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    let top = view.safeAreaInsets.top
    let bottom = view.safeAreaInsets.bottom
}

2

Tôi đang làm việc với CocoaPods khuôn khổ và trong trường hợp UIApplication.sharedkhông có sẵn sau đó tôi sử dụng safeAreaInsetstrong quan điểm của window:

if #available(iOS 11.0, *) {
    let insets = view.window?.safeAreaInsets
    let top = insets.top
    let bottom = insets.bottom
}

2

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

2

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

1

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)


1
UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 
CGFloat fBottomPadding = window.safeAreaInsets.bottom;

1

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.


0

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
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.