ใน iPhone X ใหม่สิ่งที่จะเป็นวิธีที่เหมาะสมที่สุดในการรับทั้งความสูงและความสูงด้านล่างสำหรับพื้นที่ที่ไม่ปลอดภัย
ใน iPhone X ใหม่สิ่งที่จะเป็นวิธีที่เหมาะสมที่สุดในการรับทั้งความสูงและความสูงด้านล่างสำหรับพื้นที่ที่ไม่ปลอดภัย
คำตอบ:
ลองสิ่งนี้:
ในวัตถุประสงค์ 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 พื้นที่ปลอดภัยยังรวมถึง overscan ของหน้าจอซึ่งเป็นตัวแทนของพื้นที่ที่ครอบคลุมโดยกรอบของหน้าจอ
ด้านล่างพื้นที่ปลอดภัยถูกเน้นใน iPhone 8 และ iPhone X-series:
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-Series
ทีนี้ถ้าเพิ่มแถบนำทาง
ดังนั้นตอนนี้วิธีรับความสูงของพื้นที่ที่ไม่ปลอดภัยหรือไม่ เราจะใช้safeAreaInset
นี่คือวิธีแก้ไขปัญหา แต่สิ่งที่สำคัญก็คือ
คนแรก:
self.view.safeAreaInsets
ที่จะคืนค่า EdgeInsets ตอนนี้คุณสามารถเข้าถึงด้านบนและด้านล่างเพื่อทราบสิ่งที่ใส่เข้าไป
อันที่สอง:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
อันแรกที่คุณกำลังดู insets ดังนั้นหากมีแถบนำทางมันจะถูกพิจารณา แต่อันที่สองคุณกำลังเข้าถึง safeAreaInsets ของหน้าต่างดังนั้นแถบนำทางจะไม่ถูกนำมาพิจารณา
ใน iOS 11 มีวิธีการหนึ่งที่บอกว่า safeArea เปลี่ยนไปเมื่อใด
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
let top = view.safeAreaInsets.top
let bottom = view.safeAreaInsets.bottom
}
ฉันกำลังทำงานกับกรอบ CocoaPods และในกรณีที่UIApplication.shared
เป็นไม่พร้อมใช้งานแล้วฉันจะใช้safeAreaInsets
ในมุมมองของwindow
:
if #available(iOS 11.0, *) {
let insets = view.window?.safeAreaInsets
let top = insets.top
let bottom = insets.bottom
}
safeAreaLayoutGuide เมื่อมุมมองปรากฏบนหน้าจอคู่มือนี้จะแสดงส่วนของมุมมองที่ไม่ครอบคลุมโดยแถบนำทางแถบแท็บแถบเครื่องมือและมุมมองบรรพบุรุษอื่น ๆ (ใน tvOS พื้นที่ปลอดภัยแสดงพื้นที่ที่ไม่ได้อยู่ในกรอบของหน้าจอ) หากมุมมองไม่ได้ถูกติดตั้งในลำดับชั้นการดูหรือยังไม่ปรากฏบนหน้าจอ
จากนั้นเพื่อให้ได้ความสูงของลูกศรสีแดงในภาพหน้าจอ:
self.safeAreaLayoutGuide.layoutFrame.size.height
Swift 5, Xcode 11.4
`UIApplication.shared.keyWindow`
มันจะให้คำเตือนการคัดค้าน '' keyWindow 'เลิกใช้แล้วใน iOS 13.0: ไม่ควรใช้กับแอพพลิเคชั่นที่รองรับหลาย ๆ ฉากเพราะมันจะส่งคืนหน้าต่างกุญแจข้ามฉากที่เชื่อมต่อทั้งหมด' เนื่องจากฉากเชื่อมต่อ ฉันใช้วิธีนี้
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 })
}
}
Objective-C ที่มีปัญหาเมื่อkeyWindowมีค่าเท่ากับศูนย์ เพียงใส่รหัสด้านบนในviewDidAppear (ไม่ใช่ใน viewDidLoad)
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
CGFloat fBottomPadding = window.safeAreaInsets.bottom;
ส่วนขยาย 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
}
}
การขยาย UIA เป็นโปรแกรมเสริมสามารถเป็นส่วนเสริมของ 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
}
}
สำหรับผู้ที่เปลี่ยนเป็นโหมดแนวนอนคุณต้องแน่ใจว่าใช้viewSafeAreaInsetsDidChangeหลังจากการหมุนเพื่อรับค่าที่อัปเดตมากที่สุด:
private var safeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
override func viewSafeAreaInsetsDidChange() {
if #available(iOS 11.0, *) {
safeAreaInsets = UIApplication.shared.keyWindow!.safeAreaInsets
}
}