생성일: 2022년 1월 1일 오후 9:21
Main.storyboard에 위와 같이 테이블 뷰, 텍스트 뷰, 버튼을 정렬시켜 채팅 앱의 기본 구조를 만든다.
.xib 파일을 생성하여 말풍선 이미지와 텍스트 뷰를 만들고 정렬시킨다.
날짜 라벨에 superview와 ≥ 40 constraint를 주어 말풍선이 너무 커져서 넘치는 현상을 방지한다.
위와 같은 방식으로 채팅을 받는 방향의 xib파일도 추가로 생성한다.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var chatTabbleView: UITableView!
@IBOutlet weak var inputTextView: UITextView!
@IBOutlet weak var inputViewBottomMargin: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// 테이블 뷰에 커스터마이징한 xib 셀 등록
chatTabbleView.register(UINib(nibName: "MyCell", bundle: nil), forCellReuseIdentifier: "myCell")
chatTabbleView.register(UINib(nibName: "YourCell", bundle: nil), forCellReuseIdentifier: "yourCell")
// 키보드 관련 옵저버 (상태를 알려주는 것) 설정
// 키보드 올라올 때
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
// 키보드 내려올 때
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWillShow(noti: Notification) {
let notifiInfo = noti.userInfo!
let keyboardFrame = notifiInfo[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
let height = keyboardFrame.size.height
// 키보드가 올라오는 시간(애니매이션 시간)
let animationDuration = notifiInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval
UIView.animate(withDuration: animationDuration) {
// 아이폰의 safe area 영역을 더해주어야 빈공간 없이 키보드와 inputView가 붙어진다.
self.inputViewBottomMargin.constant = -height + self.view.safeAreaInsets.bottom
self.view.layoutIfNeeded()
}
}
@objc func keyboardWillHide(noti: Notification) {
let notifiInfo = noti.userInfo!
let animationDuration = notifiInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval
UIView.animate(withDuration: animationDuration) {
self.inputViewBottomMargin.constant = 0
self.view.layoutIfNeeded()
}
}
@IBAction func SendString(_ sender: Any) {
}
}
- 테이블 뷰의 인스펙터에서 Keyboard 항목을 Dismiss on drag로 바꾸어 주면 키보드 밖을 유저가 드래그 했을 때 키보드가 사라지게 됨
class ViewController: UIViewController{
@IBOutlet weak var chatTabbleView: UITableView!{
didSet{
chatTabbleView.delegate = self
chatTabbleView.dataSource = self
chatTabbleView.separatorStyle = .none
}
}
var chatData = [String]()
tableViewDelegate를 이용하기 위해 위와 같이 didSet에서 delegate를 self로 설정한다.
사용자에게 입력받은 텍스트를 저장하기 위해 chatData 어레이를 선언한다.
// 사용자가 텍스트를 입려갛고 전송 버튼을 눌렀을 때 => chatData 어레이 안에 해당 텍스트가 들어가야 함
@IBAction func SendString(_ sender: Any) {
chatData.append(inputTextView.text)
inputTextView.text = ""
// 테이블 뷰 갱신
chatTabbleView.reloadData()
// 사용자가 새로운 메시지를 전송했을 때 화면을 테이블 뷰에 새로운 메시지가 생긴 부분으로 내려주게 함
let lastIndexPath = IndexPath(row: chatData.count - 1, section: 0)
chatTabbleView.scrollToRow(at: lastIndexPath, at: UITableView.ScrollPosition.bottom, animated: true)
}
전송 버튼과 IBAction을 연결하여 사용자가 전송 버튼을 누르면 테이블 뷰를 갱신하고 새 메시지가 생긴 부분으로 화면을 옮기도록 구현했다.
//MARK: - TableViewDelegate
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return chatData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row % 2 == 0 {
let myCell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCell
myCell.myTextView.text = chatData[indexPath.row]
return myCell
}else{
let yourCell = tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath) as! YourCell
yourCell.yourTextView.text = chatData[indexPath.row]
return yourCell
}
}
}
extension을 사용하여 UITableViewDelegate와 DataSource를 불러왔다.
두 프로토콜은 위의 두 함수를 필수적으로 필요로 한다.
현재 구현하고자 하는 채팅 앱에서는 타인과 채팅을 치는 것이 아닌 본인만 채팅을 하도록 구성했기 때문에 위와 같이 chatData 어레이의 짝수 인덱스 텍스트는 myCell을, 홀수 인덱스 텍스트는 yourCell을 사용하도록 했다.
사용자가 많은 글자를 Input Text View에 입력하고 있다면 해당 Text View의 높이를 키워서 사용자가 해당 시기까지 입력한 텍스트들을 한눈에 확인할 수 있도록 구현해보자
class ViewController: UIViewController{
@IBOutlet weak var chatTabbleView: UITableView!{
didSet{
chatTabbleView.delegate = self
chatTabbleView.dataSource = self
chatTabbleView.separatorStyle = .none
}
}
var chatData = [String]()
@IBOutlet weak var inputTextView: UITextView!{
didSet{
inputTextView.delegate = self
}
}
@IBOutlet weak var inputTextViewHeight: NSLayoutConstraint!
UITextViewDelegate를 사용하기 위해 위와 같이 inputTextView.delegate를 self로 선언한다.
inputTextView의 높이를 변경해야 하기 때문에 해당 constraint를 IBOutlet으로 가져온다.
//MARK: - UITexViewDelegate
extension ViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
if textView.contentSize.height <= 40{
inputTextViewHeight.constant = 40
}else if textView.contentSize.height >= 100 {
inputTextViewHeight.constant = 100
}else {
inputTextViewHeight.constant = textView.contentSize.height
}
}
}
100 px 까지는 사용자가 많이 입력하면 그만큼 inputTextView의 높이를 키우도록 구현했다.