ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Swift/Clone] 카카오톡 대화창 클론 코딩1
    Study/ios 2023. 7. 11. 21:24

    요즘에 어플에 있는 기능들을 보고 구현해보고 싶은 기능들을 적어놓고 하나씩 따라서 구현해보고 있다.

    오늘은 카카오톡 대화창을 최대한 똑같이 구현해 보려고 한다.

    일단 카톡을 보내기 위해 텍스트를 입력하는 텍스트 뷰를 구현해 보았다.

     

     

    디자인은 색과 아이콘 누끼를 따서 똑같이 구현했고

    주요 기능을 설명해보겠다.

     

    기본 디자인

    두개의 뷰와 각종 버튼 그리고 텍스트 뷰로 구성되어 아래와 같이 하단 바를 구성했다.

     

     

     

    이제 텍스트 뷰를 아래와 같이 키보드가 올라오면 같이 올라가게 해줘야 한다.

     

     

    아래와 같은 코드를 추가하여 위 작업을 완료할 수 있다.

    override func viewWillAppear(_ animated: Bool) {
        // 키보드가 나타날 때의 알림에 대해 옵저버를 등록합니다.
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardUp), name: UIResponder.keyboardWillShowNotification, object: nil)
    
        // 키보드가 사라질 때의 알림에 대해 옵저버를 등록합니다.
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardDown), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        // 등록한 옵저버를 제거합니다.
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    @objc func keyboardUp(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 + 30)
                }
            )
        }
    }
    
    @objc func keyboardDown() {
        // 키보드가 사라질 때 뷰의 변환을 초기화합니다.
        self.view.transform = .identity
    }

     

     

    전송 버튼은 간단하게 텍스트 뷰의 공백여부를 체크하고 아래와 같이 나타난다.

     

     

    이제 텍스트 뷰의 라인을 구해서 하단 바의 높이를 동적으로 변경해줘야 하는데

    텍스트 뷰의 라인은 XCode에서 제공하지 않는다.

    그래서 직접 구해줘야 한다.

     

    func numberOfLinesInTextView(textView: UITextView) -> Int {
        // 텍스트 뷰의 레이아웃 매니저를 가져옵니다.
        let layoutManager = textView.layoutManager
    
        // 전체 글리프의 수를 가져옵니다.
        let numberOfGlyphs = layoutManager.numberOfGlyphs
    
        // 라인의 범위와 라인 번호를 초기화합니다.
        var lineRange = NSRange(location: 0, length: 0)
        var lineNumber = 0
    
        // 모든 글리프에 대해 반복합니다.
        for index in 0..<numberOfGlyphs {
            // 현재 글리프의 라인 프래그먼트 사각형과 해당 라인의 범위를 가져옵니다.
            layoutManager.lineFragmentRect(forGlyphAt: index, effectiveRange: &lineRange)
    
            // 현재 인덱스가 라인의 시작 위치와 동일하다면 새로운 라인이 시작되었음을 의미하므로 lineNumber를 증가시킵니다.
            if lineRange.location == index {
                lineNumber += 1
            }
        }
    
        // 전체 라인 수를 반환합니다.
        return lineNumber
    }

    레이아웃 매니저를 통해 글리프의 위치와 범위를 파악하고

    글리프의 인덱스와 라인의 시작 위치를 비교하여 새로운 라인의 시작을 감지하여 라인 수를 계산할 수 있다.

     

     

    텍스트 뷰의 델리게이트를 사용하기 위해 채택하고

    func textViewDidChange(_ textView: UITextView)

    이 함수를 사용하여 텍스트 뷰에 텍스트 변화가 있을 때마다 처리한다.

     

     

    라인 수를 가져와 그 수의 맞게 높이를 조절한다.

    func textViewDidChange(_ textView: UITextView) {
        guard let text = textView.text else { return }
    
        var numberOfLines = numberOfLinesInTextView(textView: textView) // 현재 라인 수
        // preLines는 한 번의 액션 전의 라인 수
    
        let num:CGFloat = 15
        if(numberOfLines == 0) {
            numberOfLines = 1
        }
    
        // 현재 라인 수가 전의 라인 수와 다를 경우에 높이 변경
        if(numberOfLines != preLines) {
            if(numberOfLines > preLines && numberOfLines <= 4) { // 라인 수가 늘어났을 때 // 높이 변경은 최대 4칸까지
                bottomBar.frame.origin.y -= num // 하단 뷰의 위치 변경
                bottomBarHeight.constant += num // 하단 뷰의 오토레이아웃 높이 변경
    
                textBarHeight.constant += num // 텍스트 뷰를 품고 있는 뷰의 오토레이아웃 높이 변경
            } else if(numberOfLines < preLines && numberOfLines < 4) { // 라인 수가 줄어들었을 때
                bottomBar.frame.origin.y += num
                bottomBarHeight.constant -= num
    
                textBarHeight.constant -= num
            }
    
        }
    
        preLines = numberOfLines
    
        // 전송버튼 여부
        if text.count > 0 {
            btn.isHidden = true
            btnSend.isHidden = false
        } else {
            btn.isHidden = false
            btnSend.isHidden = true
        }
    }

    카카오톡과 똑같이 라인 수가 5가 넘어가면 높이는 바뀌지 않고 스크롤로 전환된다.

     

     

    결과 화면 (라인 수 3)

     

     

    결과 화면 (라인 수 4이상)

    이 때 플러스 버튼, 이모티콘 버튼, 전송 버튼은 오토레이아웃으로 바닥에 고정하였다.

Designed by Tistory.