Skip to content

Commit 1a5b1ff

Browse files
design: 채팅 대화방 UI 개선
figma 명세를 따르도록 UI를 개선했습니다.
1 parent 9a9d9f2 commit 1a5b1ff

2 files changed

Lines changed: 64 additions & 36 deletions

File tree

Koin/Presentation/Chat/Chat/ChatHistoryTableView/ChatHistoryTableView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ extension ChatHistoryTableView: UITableViewDataSource {
9797
let lastRow = chatSections[lastSection].messages.count - 1
9898
let lastIndexPath = IndexPath(row: lastRow, section: lastSection)
9999

100+
layoutIfNeeded()
100101
DispatchQueue.main.async {
101102
self.scrollToRow(at: lastIndexPath, at: .bottom, animated: animated)
102103
}

Koin/Presentation/Chat/Chat/ChatViewController.swift

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ final class ChatViewController: UIViewController, UITextViewDelegate, PHPickerVi
1919
private var textViewHeightConstraint: NSLayoutConstraint!
2020

2121
// MARK: - UI Components
22+
private let bottomBackgrounView = UIView().then {
23+
$0.backgroundColor = .appColor(.neutral100)
24+
}
2225
private let messageInputView = UIView().then {
23-
$0.backgroundColor = UIColor.systemGray6 // ✅ 회색 배경 설정
24-
$0.layer.cornerRadius = 16
25-
$0.layer.masksToBounds = true
26+
$0.backgroundColor = .appColor(.neutral100)
2627
}
2728

2829
private let leftButton = UIButton().then {
@@ -32,26 +33,24 @@ final class ChatViewController: UIViewController, UITextViewDelegate, PHPickerVi
3233
private let textView = UITextView().then {
3334
$0.isScrollEnabled = false
3435
$0.font = UIFont.systemFont(ofSize: 16)
35-
$0.backgroundColor = .clear // ✅ 배경 투명
36+
$0.backgroundColor = .appColor(.neutral0)
3637
$0.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
37-
$0.layer.cornerRadius = 8
38+
$0.layer.cornerRadius = 12
39+
$0.text = "메세지 보내기"
40+
$0.textColor = .appColor(.neutral500)
3841
}
3942

4043
private let sendButton = UIButton().then {
4144
$0.setImage(UIImage.appImage(asset: .send), for: .normal)
4245
}
4346

44-
private let blockModalViewController = ModalViewController(width: 301, height: 179, paddingBetweenLabels: 12, title: "이 사용자를 차단하시겠습니까?", subTitle: "쪽지 수신 및 발신이 모두 차단됩니다.", titleColor: UIColor.appColor(.neutral700), subTitleColor: UIColor.appColor(.gray), rightButtonText: "차단하기").then {
45-
$0.modalPresentationStyle = .overFullScreen
46-
$0.modalTransitionStyle = .crossDissolve
47-
}
48-
4947
private let blockCheckModalViewController = BlockCheckModalViewController().then {
5048
$0.modalPresentationStyle = .overFullScreen
5149
$0.modalTransitionStyle = .crossDissolve
5250
}
5351

54-
private let chatHistoryTableView = ChatHistoryTableView().then { _ in
52+
private let chatHistoryTableView = ChatHistoryTableView().then {
53+
$0.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 16, right: 0)
5554
}
5655

5756
init(viewModel: ChatViewModel) {
@@ -78,7 +77,6 @@ final class ChatViewController: UIViewController, UITextViewDelegate, PHPickerVi
7877
configureView()
7978
bind()
8079
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
81-
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
8280
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
8381
view.addGestureRecognizer(tapGesture)
8482
leftButton.addTarget(self, action: #selector(leftButtonTapped), for: .touchUpInside)
@@ -102,18 +100,14 @@ final class ChatViewController: UIViewController, UITextViewDelegate, PHPickerVi
102100
private func bind() {
103101
let outputSubject = viewModel.transform(with: inputSubject.eraseToAnyPublisher())
104102
outputSubject.receive(on: DispatchQueue.main).sink { [weak self] output in
105-
guard let strongSelf = self else { return }
103+
guard let self else { return }
106104
switch output {
107-
case .showChatHistory(let chatHistory): self?.chatHistoryTableView.setChatHistory(item: chatHistory)
105+
case .showChatHistory(let chatHistory): chatHistoryTableView.setChatHistory(item: chatHistory)
108106
case .showToast(let message, let success):
109-
self?.showToast(message: message)
110-
if success { self?.navigationController?.popViewController(animated: true) }
107+
showToast(message: message)
108+
if success { navigationController?.popViewController(animated: true) }
111109
}
112110
}.store(in: &subscriptions)
113-
blockModalViewController.rightButtonPublisher.sink { [weak self] _ in
114-
guard let self = self else { return }
115-
present(blockCheckModalViewController, animated: true)
116-
}.store(in: &subscriptions)
117111

118112
blockCheckModalViewController.buttonPublihser.sink { [weak self] in
119113
guard let self else { return }
@@ -130,6 +124,8 @@ final class ChatViewController: UIViewController, UITextViewDelegate, PHPickerVi
130124
}.store(in: &subscriptions)
131125

132126
chatHistoryTableView.imageTapPublisher.sink { [weak self] imageUrl in
127+
self?.dismissKeyboard()
128+
133129
let zoomedImageViewController = ZoomedImageViewControllerB(shouldShowTitle: false)
134130
zoomedImageViewController.configure(urls: [imageUrl], initialIndexPath: IndexPath(row: 0, section: 0))
135131
self?.present(zoomedImageViewController, animated: true, completion: nil)
@@ -140,16 +136,17 @@ final class ChatViewController: UIViewController, UITextViewDelegate, PHPickerVi
140136
extension ChatViewController{
141137

142138
@objc private func sendButtonTapped() {
143-
if textView.text.isEmpty { return }
139+
if textView.text.isEmpty || textView.textColor == .appColor(.neutral500) { return }
144140
inputSubject.send(.sendMessage(textView.text, false))
145141
textView.text = ""
146-
textViewHeightConstraint.constant = 40
142+
textViewHeightConstraint.constant = 36
147143
UIView.animate(withDuration: 0.1) {
148144
self.view.layoutIfNeeded()
149145
}
150146
}
151147

152148
@objc private func leftButtonTapped() {
149+
dismissKeyboard()
153150
presentImagePicker()
154151
}
155152

@@ -182,16 +179,19 @@ extension ChatViewController{
182179
}
183180

184181
@objc private func rightButtonTapped() {
182+
dismissKeyboard()
183+
185184
present(blockCheckModalViewController, animated: true)
186185
}
187186
@objc private func keyboardWillShow(_ notification: Notification) {
188187
guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
189188
let keyboardHeight = keyboardFrame.height
190-
adjustInputViewPosition(up: true, height: keyboardHeight)
191-
}
192-
@objc private func keyboardWillHide(_ notification: Notification) {
193-
adjustInputViewPosition(up: false, height: 0)
189+
190+
UIView.animate(withDuration: 0.3) { [weak self] in
191+
self?.chatHistoryTableView.contentOffset.y += (keyboardHeight - UIApplication.bottomSafeAreaHeight())
192+
}
194193
}
194+
195195
private func adjustInputViewPosition(up: Bool, height: CGFloat) {
196196
messageInputBottomConstraint.constant = up ? -height : 0
197197
UIView.animate(withDuration: 0.3) {
@@ -201,12 +201,20 @@ extension ChatViewController{
201201
@objc internal override func dismissKeyboard() {
202202
textView.resignFirstResponder()
203203
}
204+
205+
func textViewDidBeginEditing(_ textView: UITextView) {
206+
if textView.textColor == .appColor(.neutral500) {
207+
textView.text = ""
208+
textView.textColor = .appColor(.neutral800)
209+
}
210+
}
211+
204212
func textViewDidChange(_ textView: UITextView) {
205213
let size = CGSize(width: textView.frame.width, height: .infinity)
206214
let estimatedSize = textView.sizeThatFits(size)
207215

208216
let maxHeight: CGFloat = 120
209-
let minHeight: CGFloat = 40
217+
let minHeight: CGFloat = 36
210218
if estimatedSize.height >= maxHeight {
211219
textViewHeightConstraint.constant = maxHeight
212220
textView.isScrollEnabled = true
@@ -220,12 +228,27 @@ extension ChatViewController{
220228
self.view.layoutIfNeeded()
221229
}
222230
}
231+
232+
func textViewDidEndEditing(_ textView: UITextView) {
233+
if textView.text == "" {
234+
textView.text = "메시지 보내기"
235+
textView.textColor = .appColor(.neutral500)
236+
}
237+
}
238+
239+
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
240+
if textView.text == "" {
241+
textView.text = "메시지 보내기"
242+
textView.textColor = .appColor(.neutral500)
243+
}
244+
return true
245+
}
223246
}
224247

225248
extension ChatViewController {
226249

227250
private func setUpLayOuts() {
228-
[chatHistoryTableView, messageInputView].forEach {
251+
[bottomBackgrounView, chatHistoryTableView, messageInputView].forEach {
229252
view.addSubview($0)
230253
}
231254
[leftButton, textView, sendButton].forEach {
@@ -236,29 +259,33 @@ extension ChatViewController {
236259
private func setUpConstraints() {
237260
chatHistoryTableView.snp.makeConstraints { make in
238261
make.top.leading.trailing.equalToSuperview()
239-
make.bottom.equalTo(messageInputView.snp.top).offset(-16)
262+
make.bottom.equalTo(messageInputView.snp.top)
240263
}
241264
messageInputView.snp.makeConstraints { make in
242-
make.leading.trailing.equalToSuperview().inset(8)
265+
make.leading.trailing.equalToSuperview()
243266
make.height.greaterThanOrEqualTo(50)
244-
messageInputBottomConstraint = make.bottom.equalTo(view.safeAreaLayoutGuide).constraint.layoutConstraints.first
267+
make.bottom.equalTo(view.keyboardLayoutGuide.snp.top)
245268
}
246269
leftButton.snp.makeConstraints { make in
247-
make.leading.equalTo(messageInputView).offset(8)
248-
make.centerY.equalTo(messageInputView)
270+
make.leading.equalToSuperview().offset(24)
271+
make.bottom.equalTo(messageInputView.snp.bottom).offset(-8)
249272
make.size.equalTo(32)
250273
}
251274
sendButton.snp.makeConstraints { make in
252-
make.trailing.equalTo(messageInputView).offset(-8)
253-
make.centerY.equalTo(messageInputView)
275+
make.trailing.equalToSuperview().offset(-24)
276+
make.bottom.equalTo(messageInputView.snp.bottom).offset(-8)
254277
make.size.equalTo(32)
255278
}
256279
textView.snp.makeConstraints { make in
257280
make.leading.equalTo(leftButton.snp.trailing).offset(8)
258281
make.trailing.equalTo(sendButton.snp.leading).offset(-8)
259282
make.top.equalTo(messageInputView).offset(8)
260283
make.bottom.equalTo(messageInputView).offset(-8)
261-
textViewHeightConstraint = make.height.equalTo(40).priority(.high).constraint.layoutConstraints.first
284+
textViewHeightConstraint = make.height.equalTo(36).priority(.high).constraint.layoutConstraints.first
285+
}
286+
bottomBackgrounView.snp.makeConstraints {
287+
$0.leading.trailing.bottom.equalToSuperview()
288+
$0.top.equalTo(messageInputView.snp.bottom)
262289
}
263290
}
264291
private func configureView() {

0 commit comments

Comments
 (0)