TIL(230302)

Youth·2023년 3월 2일
0

1. swift에서 빈 문자열을 확인 하는방법

여전히 카카오톡 채팅창 기능을 구현하고 있는중이다. 이게 그냥 처음에 봤을 때는 엄청 쉬워보였는데 디테일함을 살리려고 하면 생각보다 어려운거같다.

이번에 구현하려했던 기능? UX는 카카오톡에서 textView에 있는 빈문자열을 확인하는 기능이다. 왜 확인을해야하나면 첫번째로 빈문자열이면 send버튼이 없어져야한다. 그리고 메세지를 보내면 다시 textview가 비워지는데 이때도 send버튼이 없어져야한다. 그래서 처음엔

textView.text == ""
textView.text.isEmpty

이렇게 구현을 헀는데 문제가 하나 발생했다
아무것도 입력을 안하고 그냥 enter를 눌러서줄바꿈을 하면 빈문자열로 인식하느게 아니라 문자열이 있는걸로 인식을 한다

그래서 구글링을 하다가

textView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty

이런걸 알게되었는데 빈공간과 newLine(여기서는 줄바꿈을이야기하는거같다)를 제외했을때 isEmpty인지를 판달할수있는 함수가 있었다 그래서 이걸가지고 삼항연산자를 만들어서 Button의 유무를 판별했다

chatView.sendButton.isHidden = textView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? true : false

이렇게 하니 잘돌아간다


2. layoutIfNeeded()이란?

이건 내가 잘못 코딩해서 문제가 발생했을때 문제를 해결해준 방식이다.
우선 내가 카카오톡 채팅창을 구현할때 가장 골머리를 앓았던 부분은 textView의 높이가 달라지면 chatView(tableView)가 그거에따라 올라가야한다는거였다.

이틀동안 삽질을하다보니 tableView의 layout을 잘못잡았다는걸 알게되어서 해결은 했지만

하루동안 어떤 삽질이 있었는지를 기록해보면 textView에 text를 입력할때마다 호출하는 delegate 메서드가 있다

textViewDidChange(_ textView: UITextView)

이녀석인데 처음에 생각했던 방법은 text가 입력될때마다(enter를 눌러서 창크기가 커질때마다) scrollBottom함수를 호출하면 layout이 올라가게 보이지않을까?였다

그래서 매번 textViewDidChange함수가 호출될때마다 scrollBottom이라는 메서드를 호출했는데 문제는 이게 layout 업데이트가 좀 느린느낌이 들었다. 그래서 내가 뭘잘못했나 싶었는데 구글링을 하다가 view.layoutIfNeeded()를 넣으면 해당 layout을 업데이트하는 task를 가장 먼저 실행할 수있게 해준다고한다. 이렇게 하니까 내가 원하는 느낌으로 ui가 구현되었다.

하지만 문제는 저게 내가 text를 입력할때마다(한글자입력할떄마다)저게 호출이 되어서 scroll이 내려오는게 문제였다. 카카오톡에서 채팅을 쳐보면 스크롤을 올린상태에서 채팅을 친다고 스크롤이 맨아래로 내려오지 않는다...

그래서 이부분은 autoLayout을 수정하는 방향으로 구현했다


3. 키보드올리고 내리는방법(touchBegan안먹히는경우)

textField나 textView에서 가장 중요한부분은 데이터를 전달하고 보여주는것도 있지만 키보드를 어떻게 상용하는지도 매우 중요하다고 생각한다.

구글링을 해봐도 키보드를 가지고 코드를 짜는 부분은 공통적으로 한 세부분정도가있다

  1. textView를 누르면 키보드가 올라오게(혹은 view에 들어오면 자동으로 textView에 포커싱이 가서 키보드올라오게)
  2. 키보드가 올라오면 전체적으로 view가 위로올라가서 유저가 보는 view를 키보드가 가리지 않게 처리하기
  3. 키보드 이외의 영역을 터치하면 키보드가 내려가기

1번 먼저 이야기를 해보면 나같은 경우는 카카오톡 채팅을 대상으로 구현을 했기때문에 한번 카카오톡 채팅창에 들어가보니 textview에 포커싱이 바로 되서 키보드가 올라오지는 않았다. textView를 터치해야 키보드가 올라왔다.

그래서 textView delegate에 있는 함수를 이용했다

func textViewDidBeginEditing(_ textView: UITextView) {
    textView.becomeFirstResponder()
}

textView가 눌리면 실행되는 함수인데 이때 textView에 .becomeFirstResponder를 하면 키보드가 올라오게 된다.

그리고 바로 2번으로 넘어가서 키보드가 올라오고 내려오는 시점에 이벤트를 발생시키는 notification Name이 존재하기때문에

    private func addKeyboardNotifications(){
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    private func removeKeyboardNotifications(){
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification , object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }

이런식으로 addObserver를 하는 코드와 removeObserver를 하는 코드를 함수로 묶어놓고

override func viewWillAppear(_ animated: Bool) {
    self.addKeyboardNotifications()
}

override func viewWillDisappear(_ animated: Bool) {
    self.removeKeyboardNotifications()
}

viewwillappear과 viewwilldisappear에 넣어서 메모리에서 잘 해제될수있도록 만들어준다 이렇게 하면
키보드가 올라오면 UIResponder.keyboardWillShowNotification가 호출되어서 #selector(self.keyboardWillShow(:))이 함수가 호출되고 키보드가 내려가면 #selector(self.keyboardWillHide(:))함 수가 호출된다

    @objc func keyboardWillShow(_ notification: NSNotification) {
        if let keyboardFrame:NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
           let keyboardRectangle = keyboardFrame.cgRectValue
            UIView.animate(withDuration: 0.3, animations: {
                self.view.transform = CGAffineTransform(translationX: 0, y: -keyboardRectangle.height)
            })
        }
    }

    @objc func keyboardWillHide(_ notification: NSNotification) {
        self.view.transform = .identity
    }

그리고 키보드가 올라오면 키보드의 높이만큼 view의 위치를 그만큼 올려주는 코드도 작성을했다
반대로 키보드가 내려가면 view의 위치를 원래대로 돌아오게하는 코드도 작성을 했다

3번같은 경우는 보통 구글링을 하면 viewController에 touchBegan이라는 함수를 재정의해서 거기다가 view.endEditing(true)을 해주면된다고해서 그렇게 했는데 이게왠걸 안된다....

그래서 왜그럴까 생각을 해봤는데 다른 "빈"viewController의 공간을 터치해야하는건데 나는 chat창이 tableView니까 터치를 해도 안한다고 인식을 하는거다

    private func addTapGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard(_:)))
        view.addGestureRecognizer(tapGesture)
    }

그래서 그냥 view전체에다가 tapgesture를 추가해서 tapgesture가 감지될때마다

@objc private func hideKeyboard(_ sender: Any) {
    view.endEditing(true)
}

이렇게 키보드를 내릴 수있게 만들었다.


4. snapkit에서 remakeConstraints와 updateConstraints의 차이

보통 ui를 그릴때 viewdidload에서는 makeConstraints를 사용한다.
근데 ui를 업데이트하려고할대 remake가 있고 update가있길래 그동안은 그냥 아무거나써보고 오류 발생하면 다른거 쓰고 그랬는데 이참에 좀 알아봐야겠다 싶어서 알아봤다.

remakeConstraints의 경우에는 ui를 업데이트할때 말그래도 remake를 하는걸 의미한다 특정요소에 remakeConstraints를 하면 기존의 Constraints는 사라지고 새로 정의한 Constraints를 사용하게된다 그래서 제약조건을 죄다 다시 설정해줘야한다

updatdConstraints는 반대로 특정 요소, 예를들어 top이나 width height같은요소 하나만 바꿔줄수가있다 근데 updatdConstraints를 업데이트할때는 상수값만 넣을 수 있다고 한다

profile
AppleDeveloperAcademy@POSTECH 1기 수료, SOPT 32기 iOS파트 수료

0개의 댓글