안녕하세요! 오늘은 커스텀 UIInputView를 다루다가 SafeArea bottom inset을 높이에 반영하는게 잘 되지 않아서 이슈를 파헤쳐 봤습니다.
🐞 버그가 있는 코드
버그가 있는 코드 부분은 복기를 위해서 남겨둡니다. 바쁘신 분들은 무시하셔도 좋습니다.
기존에 작업해둔 코드에서는 init 시점에 UIInputView를 사용하는 UIViewController를 주입받았습니다.
init(viewController: UIViewController) {
self.viewController = viewController
}
그리고 나서 intrinsicContentSize를 계산해줬습니다.
기존에 주입 받았던 viewController를 활용해서 safeAreaInsets.bottom을 가져옵니다.
public override var intrinsicContentSize: CGSize {
return CGSize(
width: contentWidth,
height: contentHeight + (viewController?.view.safeAreaInsets.bottom ?? 0)
)
}
이렇게 하면 잘될 줄 알았는데 safe area 반영이 제대로 되지 않습니다.
키보드 높이 계산의 타이밍 이슈가 있는 것으로 추측됩니다.
제대로 된 타이밍에 유연한 높이 값을 반영하기 위해 삽질을 하게 됩니다.
비슷한 이슈를 올려둔 블로그를 발견!
padamthapa.com/blog/inputaccessoryview-ignores-safearea-iphonex/
🥳 문제 해결
핵심 아이디어는 didMoveToWindow()가 호출되었을 때 window의 safeAreaInsets를 활용한다는 것 입니다.
오토레이아웃을 제대로 사용해서 외부에서 contentSize 업데이트를 해주지 않아도 됩니다.
이 방식을 사용하면 세가지 장점이 있습니다.
1. 제대로 된 라이프 사이클을 통해서 safeAreaInsets을 활용할 수 있다.
2. UIViewController를 주입받을 필요가 없다.
3. allowSelfSizing을 활성화하고 오토레이아웃을 사용하기 때문에, 외부에서 invalidateIntrinsicContentSize()를 호출해주지 않아도 됩니다.
커스텀 UIInputView init 부분을 수정합니다.
이제는 주입받던 UIViewController가 필요 없습니다.
allowSelfSizing을 활성화해줍니다. 그리고 오토레이아웃을 지정해줍니다.
window에 add되었을 때 레이아웃 업데이트를 위해 bottomConstraint를 보관해둡니다.
public init() {
super.init(frame: .zero, inputViewStyle: .default)
allowsSelfSizing = true
translatesAutoresizingMaskIntoConstraints = false
addSubview(contentView)
contentView.snp.makeConstraints {
$0.top.leading.trailing.equalToSuperview()
bottomConstraint = $0.bottom.equalToSuperview().constraint
}
}
developer.apple.com/documentation/uikit/uiview/1622527-didmovetowindow
Apple Developer Documentation
developer.apple.com
UIView의 템플릿 메소드인 didMoveToWindow를 활용합니다.
라이프 사이클에 따라 자동으로 호출됩니다.
보관해뒀던 bottomConstraint를 업데이트 합니다.
// https://padamthapa.com/blog/inputaccessoryview-ignores-safearea-iphonex/
public override func didMoveToWindow() {
super.didMoveToWindow()
guard let window = window else {
return
}
bottomConstraint?.update(inset: window.safeAreaInsets.bottom)
}
이렇게 하면 사용하는 쪽에서는 별다른 높이 조작 없이도 bottom inset이 적용된 UIInputView를 사용할 수 있습니다!!
제 경우에는 외부 라이브러리에서 제공받은 UIInputView가 safe area bottom inset이 적용안되어 있어서 한번 감싸서 사용한 케이스 입니다. 혹시 동일한 이슈가 있는 분이라면 저처럼 UIInputView를 새로 만들어서 래핑해서 이슈 해결하는 방법도 있으니 참고하면 좋을 것 같아요.
도움이 되었으면 좋겠네요. 읽어주셔서 감사합니다 :)
'iOS' 카테고리의 다른 글
iPad Slide-over 키보드 높이 계산 이슈 해결 (0) | 2021.06.15 |
---|---|
Main Thread 무한 루프 문제해결 과정 (0) | 2021.06.14 |
Swift 객체 외부에서 객체가 해제되는 것 감지하기 (0) | 2021.04.20 |
@testable import로 연결한 모듈에서 Undefined symbol이 발생하는 이슈 대응 (0) | 2021.04.17 |
Could not insert new outlet connection 이슈 대응 (0) | 2021.02.04 |