[ios] 키보드가 TextField를 가리는 문제점 - 2
안녕하세요. 오늘은 예전에 한번 다뤄보았던 이슈 중 하나인 디바이스의 키보드가 나타났을 때 TextField
를 가리는 문제점에 대해 조금 더 공부해보았고 이를 정리하고자 글을 작서합니다. 예전에 작성한 글은 다음과 같습니다.
또한 오늘의 포스팅을 보다 쉽게 이해하시려면 Notification
에 대한 배경지식이 필요합니다. 이에 대한 글도 작성해놓았으니 참고하시기 바랍니다.
그럼 바로 시작하겠습니다.
먼저 이전에 작성한 글의 문제점을 살펴보도록 하겠습니다. 저는 해당 포스팅에서 문제의 해결방법으로 키보드가 나타나고 사라지는 이벤트에 대해 Notification
을 걸어두었고 해당 Notification
들에 대한 액션으로 메소드를 다음과 같이 정의하였었습니다.
당시 글을 작성하던 저도 공부를 막 시작한 상태였지만 제가 느끼기에도 무언가 부족하다는 것을 많이 느꼈습니다. 하지만 당장은 문제가 없으니 신경쓰지 않고 위와 같은 방법을 자주 사용하였습니다.
하지만 시간이 지나고 하나의 ViewController
위에 많은 뷰가 올라가고 뷰들이 서로 복잡한 위치 관계를 맺어가면서 해당 코드는 원인을 알 수 없는 이슈를 발생시켰습니다. 대표적인 사례가 다음과 같이 키보드가 사라졌을 때 뷰가 완전히 내려가지 않는 이슈였습니다.
해당 이슈의 원인과 이를 해결하기 위해서 많은 노력을 했지만 정확히 무엇이 잘못되었다라고 결론을 내리지 못하였습니다. 하지만 뷰의 y좌표를 바꾸어 직접적으로 뷰를 올리고 내리는 코드는 분명히 바람직한 솔루션은 아니라는 것은 느꼈습니다. 그것도 -150이라는 임의의 값으로 말이죠
그래서 이를 해결하기 위해 구글링과 유투브의 여러 코드들을 살펴보았으나 ScrollView
를 추가하여 키보드 이벤트가 발생하면 TextField
를 스크롤 시켜주어 이슈를 해결하는 방법들이었습니다.
하지만 이 이슈를 해결하기 위해 ScrollView
를 전체 뷰에 추가시켜주는 것은 뭔가 과하다고 생각이 들었고 다른 솔루션들을 생각해보았습니다. 그리하여 처음에 생각한 것이 바로 CGAffineTransform
입니다.
CGAffineTransform
CGAffineTransform
은 간단히 얘기해보자면 3 by 3 행렬을 바탕으로 iOS 화면 위에 그려질 그래픽 요소들을 회전, 스케일, 이동 그리고 뷰를 비틀때 사용합니다. 보다 자세한 내용과 사용법은 다음의 링크를 참고해주세요.
저는 이를 이용해서 뷰를 위로 이동시키고자 했습니다. 그리고 보다 정확한 수치를 사용하기 위해 Notification
이벤트가 발생했을 때 해당 이벤트에 대한 정보를 담은 매개변수를 이용하였습니다.
- 여기서
notification
은 키보드가 나타났다는 이벤트 발생에 대한 정보를 담고있기 때문에 기본적으로 해당 키보드의 프레임과 해당 키보드가 위로 올라오는 애니매이션의 속도 등을 비롯한 여러 정보를 담고 있고 이는 userInfo
라는 해시에 담겨있어 적절한 Key
값을 통해 값에 접근할 수 있습니다.
- 그리고 저는 이렇게 얻은 키보드의 높이 값과
CGAffineTransform
을 이용해 뷰를 키보드의 높이만큼 올려주었습니다.
- 그리고
.identity
를 통해 키보드가 밑으로 내려갔을 때 뷰는 자신의 원래 모습으로 돌아갈 수 있습니다. 이를 통해 뷰의 transform
은 본인의 원래 상태를 기억할 수 있다는 것을 알 수 있었습니다.
이렇게 저는 CGAffineTransform
을 이용하는 것이 바람직하다고 생각하였습니다.
하지만 이렇게 ViewController
의 루트 뷰를 변환시키는 것은 잠재적인 이슈를 발생시킨다는 글을 보고 다른 방법을 찾아보게 되었고 저는 현재 ViewController
의 루트 뷰의 서브 뷰들을 하나의 뷰 아래에 모두 넣고 이 뷰를 루트 뷰의 서브 뷰로 만드는 방법을 생각했습니다.
ViewController — Root View( View ( Subviews ) )
먼저 이 방법을 생각해봤을 때 가장 먼저 든 생각이 이미 Auto Layout이 설정되어 있는 뷰들을 하나의 뷰에 어떻게 모두 담지? 라는 생각이었습니다. 하지만 XCode는 생각보다 강력하더군요! 방법은 의외로 간단했습니다. 하나의 ViewController
의 루트 뷰의 서브 뷰들을 모두 선택하시고 상단의 메뉴에서 Editor -> Embed In -> View 를 선택하시면 됩니다.
기존의 Auto Layout 때문에 이슈가 발생합니다.
서브 뷰들을 담게 되는 뷰에 Auto Layout을 적용시킵니다.
그리고 서브 뷰들을 담게 되는 뷰에 맞춰서 서브 뷰들에 Auto Layout을 적용시킵니다.
마지막으로 서브 뷰들을 담게 되는 뷰의 bottom constraint
을 해당 ViewController
에 @IBOutlet
으로 연결합니다. 이제 해당 Constraint
에 접근하여 constant
값을 바꿔주어 뷰의 좌표를 바꾸는 것이 아닌 뷰의 상대적인 크기를 줄이고 늘릴 수 있습니다. 아래는 이에 대한 코드입니다.
이번 솔루션에는 중복되는 코드가 많아 메소드로 빼서 작성하였습니다.
- 먼저
CGAffineTransform
을 사용했을 때의 방법처럼 userInfo
를 받아와 키보드의 높이와 애니매이션 속도를 받아왔습니다. 그리고 키보드가 나타나는 이벤트인지 사라지는 이벤트인지에 따라 새로 만든 서브 뷰들을 담는 뷰의 bottom constraint
를 가리키는 inputViewBottomAnchor
의 constant
값을 바꿔주었습니다.
마무리
오늘은 이렇게 키보드가 TextField
를 가리는 이슈를 해결하기 위한 여러가지 방법에 대해 공부해보았습니다. 확실히 이전에 작성했던 글에 사용했던 방법보다 정교하고 정확하게 문제를 해결할 수 있었던 것 같았고 많은 개념들을 배워볼 수 있는 기회였습니다.
다음엔 더 유익한 내용으로 찾아뵙도록 하겠습니다. 감사합니다.