이전글: 2024.08.20 - [🔨 개발/📱 swift] - swift를 이용한 iOS 앱 개발 입문 3: 'IBOutlet'과 'IBActon'

UITextField를 사용해서 유저로부터 텍스트 입력을 받는 기능을 만들게 되었다. 아마 거의 모든 앱에서 사용될 기능이 아닐까 싶은데, 생각보다 간단하면서도 그와중에 고군분투의 흔적이 있어 기록으로 남겨본다.

 

1. storyboard에서 UITextField 생성하고 배치하기

Text Field를 storyboard에 추가
Text Field를 storyboard에 추가

필요한 UI 요소를 드래그앤드롭으로 storyboard에 추가하고 constraints를 설정하여 원하는 위치에 예쁘게 배치해준다. 이제는 기계적으로 하는 작업이 되어버렸다. 그리고 storyboard 옆에다 ViewController를 열어 control+드래그로 IBOutlet을 연결한다. IBOutlet의 사용방법을 모른다면 이전 글을 참고하자. commentText라는 변수명으로 ViewController 파일에서 댓글 텍스트 영역을 동적으로 접근하고자 한다.

@IBOutlet weak var commentText: UITextField! // 댓글 텍스트 영역


2. 각종 꾸미기 옵션들(borderWidth, borderColor, placeholder)

이제 commentText를 예쁘게 꾸며주는 몇 가지 작업들을 거쳐본다. viewDidLoad() 메서드 내부에 borderwidth, borderColor를 설정하는데, borderColor라는 변수는 휴대폰 기기의 설정이 다크 모드이면 흰색, 라이트 모드이면 검은색으로 주었다. 그리고 placeholder 역시 UITextField가 비활성화 된 상태와 활성화 된 상태를 다르게 설정하였다. 매번 isEnabled를 바꿔줄때마다 updateCommentPlaceholder() 메서드를 수동으로 호출해야 하는지는 의문이지만, 추후에 공부하기로 하고 일단은 번거로워도 그냥 호출한다.

@IBOutlet weak var commentDiv: UIView! // 댓글 영역
@IBOutlet weak var commentImg: UIImageView! // 댓글 흑백 아이콘
@IBOutlet weak var commentText: UITextField! // 댓글 텍스트 영역

override func viewDidLoad() {
	// 생략
    
    let borderColor = UIColor { traitCollection in
            return traitCollection.userInterfaceStyle == .dark ? UIColor.white : UIColor.black
        }
    
    // 댓글 텍스트 영역의 border 옵션 설정
    commentText.layer.borderWidth = 1.0
    commentText.layer.borderColor = borderColor.cgColor
    
    commentText.isEnabled = false // 최초에는 비활성화 된 상태로 시작
    self.updateCommentPlaceholder()
}

func updateCommentPlaceholder() {
    if commentText.isEnabled {
        commentText.placeholder = "00님의 생각을 공유해주세요."
    } else {
        commentText.placeholder = "선택지를 고른 후 댓글을 남길 수 있어요."
    }
}

이제 여기까지 왔으면 꽤 멀쩡해보이는 화면이 만들어 진 듯하다.

 

3. 가상 키보드 높이를 감지하여 위치 이동하기

UITextField에 별도의 IBAction을 지정하지 않아도 기본적으로 터치 시, 아이폰의 가상 키보드를 열어주는 역할을 하나보다. 그런데 문제는, 화면의 최하단(정확히 말하면 safearea의 최하단)에 고정되어 있는 UITextField가 가상 키보드가 나타나면서 같이 따라 올라오지 않아 눈에 보이지 않는다는 것이다. 이를 NotificationCenter를 이용해서 해결한 방법을 공유하고자 한다.

override func viewDidLoad() {
    super.viewDidLoad()

    // 생략

    // 댓글 영역 배경색 설정
    commentDiv.backgroundColor = UIColor { traitCollection in
            return traitCollection.userInterfaceStyle == .dark ? UIColor.black : UIColor.white
    }
    // 댓글 영역 그림자 설정
    self.addShadowToCommentDiv(view: commentDiv)

    // 댓글 텍스트 영역의 border 옵션 설정
    commentText.layer.borderWidth = 1.0
    commentText.layer.borderColor = borderColor.cgColor

    // 키보드 프레임 변경 노티피케이션 등록
    NotificationCenter.default.addObserver(self, selector: #selector(updateKeyboardFrame(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
            
    commentText.delegate = self // 텍스트 필드 델리게이트 설정

}

@objc func updateKeyboardFrame(_ notification: NSNotification) {
    if let userInfo = notification.userInfo,
       let keyboardFrameEnd = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
       let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
       let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber {

        // Safe Area Insets 가져오기
        let bottomSafeAreaInset = view.safeAreaInsets.bottom

        // 키보드 높이에서 Safe Area를 고려한 높이 계산
        let adjustedKeyboardHeight = keyboardFrameEnd.height - bottomSafeAreaInset

        // 키보드 애니메이션의 곡선 및 지속 시간 가져오기
        let animationCurveRaw = animationCurveRawNSN.uintValue
        let animationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw << 16)

        // commentDiv의 위치를 키보드 높이에 맞춰 애니메이션으로 변경
        UIView.animate(withDuration: animationDuration,
                       delay: 0,
                       options: animationOptions,
                       animations: {
                           if keyboardFrameEnd.origin.y >= UIScreen.main.bounds.height {
                               // 키보드가 내려간 상태면 원래 위치로 복원
                               self.commentDiv.transform = .identity
                           } else {
                               // 키보드가 나타난 상태면 Safe Area를 고려하여 올리기
                               self.commentDiv.transform = CGAffineTransform(translationX: 0, y: -adjustedKeyboardHeight)
                           }
                       }, completion: nil)
    }
}

먼저 Notification 등록은, 키보드 프레임이 변결될 때 발생하는 노티피케이션으로, 키보드의 크기나 위치가 변경될 때(ex: 키보드가 올라오거나 내려갈 때, 혹은 회전할 때 등)에 해당 이벤트를 감지할 수 있다. UIResponder.keyboardWillChangeFrameNotification 외에도 UIResponder.keyboardWillShowNotification과 UIResponder.keyboardWillHideNotification을 사용하는 방법도 있지만, 이렇게 해보니 가상키보드의 영역이 입력 도중 변화할 때(ex. 한국어 -> 일본어로 언어 전환을 하면 일본어 키보드가 한자변환 추천 영역 때문에 한국어나 영어보다 사이즈가 크다. 또한 이모지 입력시에도 키보드 크기가 변화한다)를 감지하지 못하는 문제가 있었다.

가상키보드 영역의 높이를 감지해서 IBOutlet으로 연결한 UIView(또는 UITextField)의 위치를 updateKeyboardFrame 이라는 메서드에서 변경해준다. 이때, 단순히 키보드 높이만큼 translate하는 것이 아니라, SafeAreaInset을 고려해서 높이를 조정한다. 그냥 위로 올려버리면 텍스트 입력 필드 아래에 빈 공간이 생기게 된다. safearea에 대해 조금 더 공부하고 별도의 포스팅으로 올려야겠다.

 

4. 입력 완료 이후 가상 키보드 내리기

이제 유저가 텍스트 입력을 완료하면, 가상 키보드를 적절한 방식으로 없애 주어야 하는데, 이 부분이 잘 처리되지 못한 앱을 종종 본 적이 있다. 나는 우선 유저가 UITextField 외의 다른 영역을 터치하면 키보드가 사라지는 방식을 사용했다.

// 화면의 빈 공간을 터치하면 호출되는 메서드
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    // 현재 화면에서 키보드를 숨김
    self.commentText.endEditing(true)
}

이 외에도 아래로 스와이프를 통해 키보드를 내리는 카카오톡, 리턴(엔터)키 입력으로 텍스트를 전송하며 키보드를 사라지게 하는 방식 등 다양한 방식들이 사용되고 있다. 스와이프로 키보드를 내리는 방식은 구현에 도전해봐야겠다.

 

728x90

+ Recent posts