답변:
이 시도 :
에서 목표 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에 액세스하므로 탐색 막대가 고려되지 않습니다
스위프트 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 })
}
}
스위프트 5 확장
이것은 Extension으로 사용될 수 있으며 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
}
}
가로 모드로 변경하는 사용자의 경우 회전 후에 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
}
}