안전한 지역 삽입 상하 높이 확보


178

새로운 iPhone X에서 안전하지 않은 지역의 상단 및 하단 높이를 얻는 가장 적절한 방법은 무엇입니까?

여기에 이미지 설명을 입력하십시오

답변:


366

이 시도 :

에서 목표 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
}

1
@KrutikaSonawala bottomPadding + 10 (당신의 가치)
user6788419

12
이것은 작동하지 않는 것 같습니다-UIApplication.shared.keyWindow? .safeAreaInsets 값을 인쇄하면 0이 모두 반환됩니다. 어떤 아이디어?
Hai

2
필자는 모든보기의 safeAreaLayoutGuides에 대해 제한 할 수 있지만 키 창의 safeAreaInsets 값을 직접 인쇄하면 제로 교정을 반환합니다. 키 창에 추가되었지만 루트보기 컨트롤러의보기 계층 구조에없는보기를 수동으로 제한해야하기 때문에 문제가됩니다.
Hai

6
나를 위해, 나는 keyWindow항상 nil그것을 변경 하고 옵션 체인에서 체인을 windows[0]제거 ?한 다음 효과가있었습니다.
George_E

2
컨트롤러의 뷰가 이미 나타난 경우에만 작동합니다.
Vanya

85

레이아웃 가이드 사이의 높이를 얻으려면 방금 수행하십시오.

let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height

그래서 full frame height = 812.0,safe area height = 734.0

아래는 녹색 뷰의 프레임이있는 예입니다 guide.layoutFrame

여기에 이미지 설명을 입력하십시오


그게 내가 생각한 리드였습니다. 더 안정적인 솔루션이 없다고 생각합니다. 그러나 이것으로 나는 안전하지 않은 전체 높이 만 얻습니다. 각 지역에 두 개의 높이가 없습니까?
Tulleb

2
조회수가 완료되면 정확한 답변을 얻을 수 있습니다
Ladislav

2
실제로 @ user6788419의 답변도 멋지고 짧아 보입니다.
Tulleb

컨트롤러의보기를 사용하여 해당 코드로 높이가 812.0입니다. UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame안전 프레임이 있는를 사용 하고 있습니다.
Ferran Maylinch

1
@ FerranMaylinch 높이를 어디에서 확인하고 있는지 확실하지 않지만 812가 있었는데보기가 표시되기 전에 값을 확인했기 때문입니다. 이 경우 높이는 "보기가 현재보기 계층 구조에 설치되어 있지 않거나 화면에 아직 표시되지 않은 경우 레이아웃 안내 모서리는보기의 모서리와 같습니다" developer.apple.com/documentation/ uikit / uiview /…
Nicholas Harlen

81

스위프트 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
}

3
대신 let topSafeArea : CGFloat를 사용하여 선언하십시오!
Gusutafu

viewDidLayoutSubviews여러 번 호출 할 수있는 사용을 피 하십시오. 여기에서도 작동하는 솔루션은 viewDidLoad다음 과 같습니다. stackoverflow.com/a/53864017/7767664
user924

@ user924 다음 값이 업데이트되지 않습니다 경우 (즉, 이중 높이의 상태 표시 줄) 안전 지역 변화
BSOD

@bsod 그렇다면 stackoverflow.com/a/3420550/7767664 가 더 좋을 것입니다 (다른보기가 아닌 상태 표시 줄에만 해당)
user924

또는 앱 대리자를 거치지 않고 뷰 컨트롤러에 내장 된 안전 영역 API Apple을 사용할 수 있습니다.
bsod

21

여기에 다른 답변은 없었지만 이것은 효과가 없었습니다.

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
코드는 뷰 컨트롤러 수명주기의 어느 부분에서나 작동합니다. view.safeAreaInsets.top을 사용하려면 viewDidAppear 이상에서 호출해야합니다. viewDidLoad에서 view.safeAreaInsets.top은 아직 초기화되지 않았으므로 올바른 값으로 얻을 수 없습니다.
user3204765

1
최고의 답변
Rikco

17

도움을 주신 모든 분들께 감사드립니다.

그러나 안전 영역 주제가 약간 혼란스러워서 잘 문서화되지 않은 것으로 보입니다.

내가 여기를 요약 있도록 가능한 한 박쥐 우산으로 쉽게 이해할 수 있도록하기 위해 safeAreaInsets, safeAreaLayoutGuide그리고 LayoutGuide.

iOS 7에서 Apple은의 topLayoutGuidebottomLayoutGuide속성을 소개했습니다 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

iOS 11에는 safeArea가 언제 변경되었는지 알려주는 방법이 있습니다.

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

2

나는 CocoaPods의 프레임 워크 및 사례에서 일하고 UIApplication.shared있다 사용할 수없는 다음 내가 사용하는 safeAreaInsets뷰의에서 window:

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

2

safeAreaLayoutGuide 보기가 화면에 표시되면이 안내서는 탐색 막대, 탭 막대, 도구 모음 및 기타 조상보기로 다루지 않는보기 부분을 반영합니다. tvOS에서 안전 영역은 화면의 베젤을 덮지 않은 영역을 반영합니다.보기가 현재보기 계층 구조로 설치되어 있지 않거나 화면에 아직 표시되지 않으면 레이아웃 안내선 가장자리는보기의 가장자리와 같습니다.

그런 다음 스크린 샷에서 빨간색 화살표의 높이를 얻으려면 다음과 같습니다.

self.safeAreaLayoutGuide.layoutFrame.size.height

2

스위프트 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 })
    }
}

1

Objective-C keyWindownil 일 때 문제가 발생한 사람 . viewDidAppear (viewDidLoad가 아닌)에 위의 코드를 넣으십시오.


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

1

스위프트 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의 확장 또는 선호되는 것이거나 더 나은 전역 함수일 수 있습니다.


0

더 둥근 접근법

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

0

가로 모드로 변경하는 사용자의 경우 회전 후에 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
        }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.