iOS 11 safeArea 상세 설명 및 아이 폰 X 적합
현재 아이 폰 X 의 적합 에 대해 흔히 볼 수 있 는 방법 은 네 비게 이 션 표시 줄 이나 tabbar 에 고정된 거 리 를 추가 하 는 것 이다.예 를 들 어 상단 에 44pt 를 증가 하고 바닥 에 34pt 를 증가 하 는 것 이다.이런 사거 리 를 쓰 는 방법 은 언뜻 보기 에는 매우 간단 하지만,사실은 결코 좋 지 않다.이 유 는 다음 과 같다.
여기 서 저 는 safearea LayoutGuide 와 safearea Insets 를 어떻게 사용 하 는 지 동태 적 인 방식 으로 아이 폰 X 심지어 후속 적 인 모든 기종 의 적합 문 제 를 영원히 해결 하 는 지 연구 하고 싶 습 니 다.
safeAreaLayoutGuide
우선 safearea LayoutGuide 가 뭔 지 봅 시다.
복잡 해 보이 지만 사실은 간단 합 니 다.몇 가 지 를 요약 하 겠 습 니 다.
safearea LayoutGuide 는 상대 적 으로 추상 적 인 개념 입 니 다.이해 하기 쉽 도록 safearea LayoutGuide 를'view'로 볼 수 있 습 니 다.이'view'시스템 은 자동 으로 bounds 를 조정 하여 아이 폰 X 의 앞머리 구역 과 아래쪽 의 철봉 구역 을 포함 하여 각종 기괴 한 것 에 가 려 지지 않도록 합 니 다.이"view"에 서 는 모든 내용 을 완전 하 게 표시 할 수 있다 고 볼 수 있 습 니 다.
아래 녹색 부분 은 현재 컨트롤 러 view 의 safearea LayoutGuide 영역 입 니 다.
아이 폰 X 세로 화면 safearea LayoutGuide 의 bounds.png
iPhone X 가로 화면 safearea LayoutGuide 의 bounds.png
캡 처
그러나 명심 해 야 할 것 은 이'view'가 우리 의 보기 단계 에 나타 나 지 않 는 다 는 것 이다.
UILayoutGuides will not show up in the view hierarchy, but may be used as items in an NSLayoutConstraint and represent a rectangle in the layout engine.
내 가 보기에 그의 가장 큰 역할 은 참조물 로 서 view 가 특정한 view 의 safearea LayoutGuide 에 비해 구 조 를 하여 view 가 정상 적 이 고 안전하게 표 시 될 수 있 도록 하 는 것 이다(상대 적 으로 그 view 가 반드시 부모 view 가 되 는 것 은 아니다).
흔히 볼 수 있 는 사용 장면 에서 예전 에 제 view 는 컨트롤 러 view 에 비해 구 조 를 했 는데 지금 은 컨트롤 러 view 의 safearea LayoutGuide 에 비해 구 조 를 했 습 니 다.
예전 에는 이렇게 썼어 요.
[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.vc.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
지금 은 그렇습니다.
[NSLayoutConstraint constraintWithItem:someView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.vc.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
전후의 효과 에 적합 하 다
적합 전.png
view 에 대비 한 safearea LayoutGuide 로 바 꾼 후-세로.png
view 에 대비 한 safearea LayoutGuide 로 변경 한 후-가로 화면.png
이 를 통 해 알 수 있 듯 이 같은 레이아웃 에서 가로 화면 은 status bar 가 없 을 때 상단 에서 0,왼쪽 은 44 이 고 status bar 가 있 으 면 상단 에서 20 이다.어쨌든 우리 가 상대 적 으로 safearea LayoutGuide 를 배치 하면 우리 의 view 는 안전 하고 완전 하 게 표 시 될 수 있다.
그럼 iOS 11 아니면 어 떡 해?
iOS 11 이 아니면 view 에 만 레이아웃 을 할 수 있 습 니 다.레이아웃 코드 두 세트 를 써 야 합 니 다.잠시 후에 소개 하 겠 습 니 다.
이 정도 면 모든 상황 에 대처 하기에 충분 하지 않 을까요?
결코
사용자 정의 view 는 화면 가장자리 에 바짝 붙 어야 합 니 다.예 를 들 어 제 프로젝트 는 사용자 정의 네 비게 이 션 표시 줄 입 니 다.상단 은 화면 상단 에 붙 어 있 습 니 다.그러면 네 비게 이 션 표시 줄 은 view 의 safearea LayoutGuide 레이아웃 과 비교 할 수 없습니다.그렇지 않 으 면 상단 이 비어 있 습 니 다.
프레임 레이아웃
이제 safearea Insets 가 역할 을 할 차례 입 니 다.
safeAreaInsets
safearea Insets 의 공식 설명 을 살 펴 보 겠 습 니 다.
혹시 safearea Layout Guide 랑 비슷 하지 않 아 요?safearea LayoutGuide 는 safearea Insets 에 따라 자신의 bounds 를 조정 할 수 있 습 니 다.
아이 폰 X 세로 화면 에서 전체 화면 을 차지 하 는 컨트롤 러 view 의 safearea Insets 는(44,0,34,0),가로 화면 은(0,44,21,44),inset 뒤의 영역 은 safearea LayoutGuide 영역 입 니 다.
그렇다면 사용자 정의 상단 네 비게 이 션 바 에 있어 서 네 비게 이 션 바 의 높이 에 vc.view.safearea Insets.top 을 추가 하여 그 를 좀 높 게 만 들 면 된다.이렇게 X 에서 세로 화면 시 top=44,가로 화면 시 top=0,네 비게 이 션 바 의 높이 는 변화 에 응 할 수 있다.
주의해 야 할 것 은 safearea LayoutGuide 든 safearea Insets 든 모두 iOS 11 에서 만 사용 할 수 있다 는 것 이다.
safearea Insets 에 대해 서 는 버 전 판단 을 함수 에 쓸 수 있 습 니 다.
우 리 는 이렇게 쓸 수 있다.
static inline UIEdgeInsets sgm_safeAreaInset(UIView *view) {
if (@available(iOS 11.0, *)) {
return view.safeAreaInsets;
}
return UIEdgeInsetsZero;
}
UIEdgeInsets safeAreaInsets = sgm_safeAreaInset(self.view);
CGFloat height = kDefaultTopViewHeight; // , 44.0
height += safeAreaInsets.top > 0 ? safeAreaInsets.top : 20.0; // 20.0 statusbar
문제 가 또 생 겼 습 니 다.이 코드 는 어디 에 두 면 적당 합 니까?앞에서 공식 문서 에서 언급 했 듯 이 view 가 화면 에 없 거나 레이 어 링 을 표시 하지 않 으 면 view 의 safearea Insets=UIEdgeInsets Zero 이기 때문에 safearea Insets 가 바 뀌 는 시 기 를 명 확 히 알 아야 합 니 다.실제 시스템 은 이미 리 셋 을 제공 했다.
UIViewController 에 대해
-(void)viewSafeAreaInsetsDidChange NS_REQUIRES_SUPER API_AVAILABLE(ios(11.0), tvos(11.0));
UIView
-(void)safeAreaInsetsDidChange API_AVAILABLE(ios(11.0),tvos(11.0));
여기 서 주로 controller 의 리 셋 을 검토 하 는데 view 의 리 셋 은 유사 하 다.controller 의 view 의 safearea Insets 가 바 뀌 면 시스템 은 view Safearea Insets Did Change 를 호출 합 니 다.자 연 스 럽 게 우 리 는 상기 코드 를 여기에 두 고 싶 을 것 이다.그러나 여기 큰 구덩이 가 있다.이 컨트롤 러 가 애니메이션 방식 으로 push 로 들 어 올 때 네 비게 이 션 바 의 높이 도 애니메이션 으로 높 아 지고 불필요 한 애니메이션 이 생 긴 다 는 것 을 알 게 될 것 이다.이런 체험 은 매우 나쁘다.그렇다면 어디 에 두 어야 할 까?우 리 는 새로운 view Controller 호출 시 서 를 볼 필요 가 있다.
다음은"rootVC"push 에서"pushVC"콘 솔 로 출력 하 는 호출 순서 및 컨트롤 러 에 대응 하 는 view 의 safeareaInsets 입 니 다.
2017-10-04 16:59:59.594811+0800 XXX[15662:803767] Begin pushViewController to [<_TtCC8XXXTests27ContainerViewControllerTest20MockUIViewController: 0x7f9c07b643b0>]
viewDidLoad()---Optional("pushVC")---UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
willMove(toParentViewController:)---Optional("pushVC")---UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
viewWillDisappear---Optional("rootVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewWillAppear---Optional("pushVC")---UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
viewSafeAreaInsetsDidChange()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewWillLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewWillLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidLayoutSubviews()---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidAppear---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
viewDidDisappear---Optional("rootVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
didMove(toParentViewController:)---Optional("pushVC")---UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
2017-10-04 16:59:59.604563+0800 XXX[15662:803767] Did PushViewController [<_TtCC8XXXTests27ContainerViewControllerTest20MockUIViewController: 0x7f9c0790d170>]->[<_TtCC8XXXTests27ContainerViewControllerTest20MockUIViewController: 0x7f9c07b643b0>] time = [0.009772]
view Safearea Insets DidChange 호출 시기 가 이 르 고 view WillAppear 이후 불필요 한 애니메이션 이 생 긴 이 유 를 알 수 있 습 니 다.또한"pushvc"의 safeareaInsets 는 view SafeareaInsets DidChange 호출 전 까지 UIEdgeInsets Zero 이 고 그 다음 에 야 정확 한 UIEdgeInsets(top:44.0,left:0.0,bottom:34.0,right:0.0)입 니 다.또한 view Safearea Insets Did Change 뒤에 view Did Layout Subviews 를 두 번 호출 하기 때문에 높이 나 레이아웃 을 바 꾸 는 코드 를 모두 view Did Layout Subviews 에 써 야 합 니 다.그러면 불필요 한 애니메이션 효과 가 없 을 것 입 니 다.view Did Layout Subviews 는 다른 작업 에 의 해 자주 실 행 될 수 있 으 므 로 safeArea 레이아웃 을 조정 하 는 코드 가 오래 걸 리 면 상태 표 시 를 추가 하여 didChange 후 한 번 만 레이아웃 조정 을 수행 하 는 것 을 고려 할 수 있 습 니 다.
마지막 코드 는 이렇게 생 겨 야 돼 요.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
UIEdgeInsets safeAreaInsets = sgm_safeAreaInset(self.view);
CGFloat height = 44.0; // , 44.0
height += safeAreaInsets.top > 0 ? safeAreaInsets.top : 20.0; // 20.0 statusbar , statusbar
if (_navigationbar && _navigationbar.height != height) {
_navigationbar.height = height;
}
전후의 효과 에 적합 하 다어울리다.
배합 후
이렇게 하면 frame 레이아웃 과 autolayot 레이아웃 의 여러 가지 상황 에 대해 동태 적 인 적합 방안 이 생 겼 습 니 다.바로 safearea LayoutGuide 와 safearea Insets 를 사용 하여 구 조 를 유연 하 게 처리 하 는 것 입 니 다.고정 거 리 를 쓰 는 것 보다 현재 와 미래의 각종 기종 이 코드 를 조합 할 수 있 고 확장 성 이 좋 습 니 다.우리 프로젝트 도 현재 이런 방법 을 사용 하고 있 습 니 다.만약 당신 의 프로젝트 가 가로 세로 화면 이나 UI 컨트롤 의 배치 가 상대 적 으로 복잡 하 다 면 safeArea 를 사용 하 는 것 을 고려 해 야 합 니 다.
참고 로 VFL 은 이미 폐 기 된 것 같 습 니 다.왜냐하면|부모 view 의 가장자리 만 표시 할 수 있 고 부모 view 의 safearea LayoutGuide 의 가장 자 리 를 표시 하 는 기호 가 하나 도 없 기 때 문 입 니 다.예전 에 우리 가 쓴 VFL 코드 는 많이 고 쳐 야 하고 고 쳐 도 매우 번 거 로 웠 습 니 다.더 이상 VFL 을 사용 하지 말 것 을 권장 합 니 다.
마지막 버 전 판단 의 문제 입 니 다.safearea Insets 와 safearea LayoutGuide 는 모두 iOS 11 의 API 입 니 다.패 키 징 을 하지 않 고 코드 에 직접 쓰 면 대량의@available 라 는 버 전 판단 문 이 나타 날 것 입 니 다.코드 에@available 가 곳곳에 있 고 붕괴 되 어 코드 의 가 독성 을 파괴 할 것 입 니 다.
제 가 예전 에 자동 레이아웃 프레임 워 크 를 썼 기 때문에 이번 에는 safearea LayoutGuide 와 버 전 판단 을 모두 안에 포 장 했 습 니 다.개인 적 으로 이 프레임 워 크 가 NSLayoutAnchor 보다 좋다 고 생각 합 니 다.주요 역할 은 레이아웃 코드 를 간소화 하 는 것 입 니 다.다음은 NSLayout Constraint 의 대 비 를 생 성 하 는 것 입 니 다.
// topLeftView top self.view safeAreaLayoutGuide top
// API
if (@available(iOS 11.0, *)) {
[NSLayoutConstraint constraintWithItem:self.topLeftView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
} else {
[NSLayoutConstraint constraintWithItem:self.topLeftView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
}
// NSLayoutConstraint-SSLayout
self.topLeftView.top_attr = self.view.top_attr_safe
비 즈 니스 코드 에 서 는 버 전 판단 이 나 오지 않 습 니 다.관심 이 있 으 시 면 해 보 세 요.이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.