From bb554f06d84155e9bf0adf038b52edd2fb0e3c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=83=9C=ED=98=B8?= Date: Thu, 11 Apr 2024 15:00:25 +0900 Subject: [PATCH 01/26] =?UTF-8?q?feat(*):=20DetailChatViewUserInputSection?= =?UTF-8?q?StackView=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChatBot/ChatBot.xcodeproj/project.pbxproj | 12 ++++ ...ailChatViewUserInputSectionStackView.swift | 65 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift diff --git a/ChatBot/ChatBot.xcodeproj/project.pbxproj b/ChatBot/ChatBot.xcodeproj/project.pbxproj index 0eb42b2c..ca0b37ed 100644 --- a/ChatBot/ChatBot.xcodeproj/project.pbxproj +++ b/ChatBot/ChatBot.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ B4B3E2C12B42D1BB00818B3C /* ChatbotMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3E2C02B42D1BB00818B3C /* ChatbotMainViewController.swift */; }; B4B3E2C62B42D1BC00818B3C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4B3E2C52B42D1BC00818B3C /* Assets.xcassets */; }; B4B3E2C92B42D1BC00818B3C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4B3E2C72B42D1BC00818B3C /* LaunchScreen.storyboard */; }; + E67855E82BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -42,6 +43,7 @@ B4B3E2C52B42D1BC00818B3C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B4B3E2C82B42D1BC00818B3C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; B4B3E2CA2B42D1BC00818B3C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailChatViewUserInputSectionStackView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -130,6 +132,7 @@ B4B3E2BB2B42D1BB00818B3C /* ChatBot */ = { isa = PBXGroup; children = ( + E67855E62BC7B31700488352 /* View */, 70E483A52BB5AB6400931F01 /* Assets */, 70E483A42BB5AB5D00931F01 /* App */, 70E483AC2BB5AE7600931F01 /* ViewModel */, @@ -142,6 +145,14 @@ path = ChatBot; sourceTree = ""; }; + E67855E62BC7B31700488352 /* View */ = { + isa = PBXGroup; + children = ( + E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */, + ); + path = View; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -223,6 +234,7 @@ 70E4839A2BB5A88000931F01 /* OpenAICheatResponseDTO.swift in Sources */, 70E483AB2BB5AC7900931F01 /* OpenAIService.swift in Sources */, 70E483A92BB5ABC400931F01 /* OpenAIEndPoint.swift in Sources */, + E67855E82BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift in Sources */, 70E483AE2BB5AEB100931F01 /* ChatViewModel.swift in Sources */, B4B3E2BF2B42D1BB00818B3C /* SceneDelegate.swift in Sources */, ); diff --git a/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift b/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift new file mode 100644 index 00000000..c0894471 --- /dev/null +++ b/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift @@ -0,0 +1,65 @@ +// +// DetailChatViewUserInputSectionStackView.swift +// ChatBot +// +// Created by 권태호 on 4/11/24. +// + +import UIKit + +class DetailChatViewUserInputSectionStackView: UIStackView { + var userInputTextView: UITextView = { + let textView = UITextView() + textView.font = UIFont.systemFont(ofSize: 16) + textView.layer.borderWidth = 0.1 + textView.layer.cornerRadius = 10 + textView.isScrollEnabled = false + return textView + }() + + private var doneButton: UIButton = { + let button = UIButton() + let image = UIImage(systemName: "arrowshape.up") + button.setImage(image, for: .normal) + button.backgroundColor = .black + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupStackView() + + } + + override func layoutSubviews() { + super.layoutSubviews() + configureLayoutSubviews() + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - configure + private func setupStackView() { + self.axis = .horizontal + self.spacing = 5 + self.distribution = .fill + self.alignment = .center + addArrangedSubview(userInputTextView) + addArrangedSubview(doneButton) + + doneButton.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + doneButton.widthAnchor.constraint(equalToConstant: 40), + doneButton.heightAnchor.constraint(equalToConstant: 40), + doneButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10), + userInputTextView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10) + + ]) + } + + private func configureLayoutSubviews() { + doneButton.layer.cornerRadius = doneButton.frame.height / 2 + } +} From 9c4788d68111eabb080d7d43098504deb05f6ce6 Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 15:22:04 +0900 Subject: [PATCH 02/26] =?UTF-8?q?feat(*):=20Cell=EA=B5=AC=ED=98=84?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20Cell=EC=9A=94=EC=86=8C=20Stack?= =?UTF-8?q?View=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CustomCel구현을 위해 MessageContent별 로직을 구현한 UIStackView 구현 --- ChatBot/ChatBot.xcodeproj/project.pbxproj | 4 ++ .../ChatBot/View/MessageCellStackView.swift | 56 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 ChatBot/ChatBot/View/MessageCellStackView.swift diff --git a/ChatBot/ChatBot.xcodeproj/project.pbxproj b/ChatBot/ChatBot.xcodeproj/project.pbxproj index ca0b37ed..49e3bf9b 100644 --- a/ChatBot/ChatBot.xcodeproj/project.pbxproj +++ b/ChatBot/ChatBot.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ B4B3E2C62B42D1BC00818B3C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4B3E2C52B42D1BC00818B3C /* Assets.xcassets */; }; B4B3E2C92B42D1BC00818B3C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4B3E2C72B42D1BC00818B3C /* LaunchScreen.storyboard */; }; E67855E82BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */; }; + E67855EA2BC7B84400488352 /* MessageCellStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E92BC7B84400488352 /* MessageCellStackView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -44,6 +45,7 @@ B4B3E2C82B42D1BC00818B3C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; B4B3E2CA2B42D1BC00818B3C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailChatViewUserInputSectionStackView.swift; sourceTree = ""; }; + E67855E92BC7B84400488352 /* MessageCellStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCellStackView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -149,6 +151,7 @@ isa = PBXGroup; children = ( E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */, + E67855E92BC7B84400488352 /* MessageCellStackView.swift */, ); path = View; sourceTree = ""; @@ -231,6 +234,7 @@ 70E483A72BB5ABBA00931F01 /* APIKeyManager.swift in Sources */, 70A610412BBE6034009B08ED /* URLRequestBuilder.swift in Sources */, 70E483A02BB5A8E700931F01 /* MessageRepository.swift in Sources */, + E67855EA2BC7B84400488352 /* MessageCellStackView.swift in Sources */, 70E4839A2BB5A88000931F01 /* OpenAICheatResponseDTO.swift in Sources */, 70E483AB2BB5AC7900931F01 /* OpenAIService.swift in Sources */, 70E483A92BB5ABC400931F01 /* OpenAIEndPoint.swift in Sources */, diff --git a/ChatBot/ChatBot/View/MessageCellStackView.swift b/ChatBot/ChatBot/View/MessageCellStackView.swift new file mode 100644 index 00000000..254bbebe --- /dev/null +++ b/ChatBot/ChatBot/View/MessageCellStackView.swift @@ -0,0 +1,56 @@ +// +// MessageCellStackView.swift +// ChatBot +// +// Created by 권태호 on 4/11/24. +// + +import UIKit + +class MessageCellStackView: UIStackView { + private let contentLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 15) + label.numberOfLines = 0 + label.layer.cornerRadius = 10 + return label + }() + + private let senderLabel: UILabel = { + let label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 18) + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupStackView() + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupStackView() { + self.axis = .vertical + self.spacing = 8 + self.distribution = .fill + self.alignment = .fill + self.addArrangedSubview(senderLabel) + self.addArrangedSubview(contentLabel) + } + + func configureStackView(with content: RequestMessageModel) { + let message = content.content + contentLabel.text = message + contentLabel.backgroundColor = content.role == .user ? .systemBlue : .systemGray + contentLabel.textColor = content.role == .user ? .black : .black + contentLabel.textAlignment = .left + senderLabel.text = content.role == .user ? "😁You" : "🤖Bot" + } + + func reset() { + contentLabel.text = nil + senderLabel.text = nil + } +} From d128b8e9a96aebd447ba8b79dc34c97ed02264dd Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 15:27:07 +0900 Subject: [PATCH 03/26] =?UTF-8?q?feat(*):=20DetailMessage=ED=99=94?= =?UTF-8?q?=EB=A9=B4CollectionViewCell=20Custom=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 채팅화면 구현을 위한 CustomCell 구현 및 autolayout적용 --- ChatBot/ChatBot.xcodeproj/project.pbxproj | 4 ++ .../DetailMessageCollectionViewCell.swift | 44 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift diff --git a/ChatBot/ChatBot.xcodeproj/project.pbxproj b/ChatBot/ChatBot.xcodeproj/project.pbxproj index 49e3bf9b..b4aed1ad 100644 --- a/ChatBot/ChatBot.xcodeproj/project.pbxproj +++ b/ChatBot/ChatBot.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ B4B3E2C92B42D1BC00818B3C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4B3E2C72B42D1BC00818B3C /* LaunchScreen.storyboard */; }; E67855E82BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */; }; E67855EA2BC7B84400488352 /* MessageCellStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E92BC7B84400488352 /* MessageCellStackView.swift */; }; + E67855EC2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855EB2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -46,6 +47,7 @@ B4B3E2CA2B42D1BC00818B3C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailChatViewUserInputSectionStackView.swift; sourceTree = ""; }; E67855E92BC7B84400488352 /* MessageCellStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCellStackView.swift; sourceTree = ""; }; + E67855EB2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailMessageCollectionViewCell.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -152,6 +154,7 @@ children = ( E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */, E67855E92BC7B84400488352 /* MessageCellStackView.swift */, + E67855EB2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift */, ); path = View; sourceTree = ""; @@ -241,6 +244,7 @@ E67855E82BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift in Sources */, 70E483AE2BB5AEB100931F01 /* ChatViewModel.swift in Sources */, B4B3E2BF2B42D1BB00818B3C /* SceneDelegate.swift in Sources */, + E67855EC2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift b/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift new file mode 100644 index 00000000..3915cf5c --- /dev/null +++ b/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift @@ -0,0 +1,44 @@ +// +// DetailMessageCollectionViewCell.swift +// ChatBot +// +// Created by 권태호 on 4/11/24. +// + +import UIKit + +class DetailMessageCollectionViewCell: UICollectionViewCell { + static var identifier = String(describing: DetailMessageCollectionViewCell.self) + + private let messageCellStackView = MessageCellStackView() + + override init(frame: CGRect) { + super.init(frame: frame) + setupMessageCollectionViewCell() + + } + + override func prepareForReuse() { + super.prepareForReuse() + messageCellStackView.reset() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupMessageCollectionViewCell() { + contentView.addSubview(messageCellStackView) + messageCellStackView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + messageCellStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), + messageCellStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), + messageCellStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), + messageCellStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10) + ]) + } + + func configureMessageCollectionViewCell(with model: RequestMessageModel) { + messageCellStackView.configureStackView(with: model) + } +} From 661714b880b4696d0e10fa246252d0de3e06b646 Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 15:32:14 +0900 Subject: [PATCH 04/26] =?UTF-8?q?feat(*):=20=EC=B1=84=ED=8C=85=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20messageCollectionView=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 가변적인 cell높이를 구현하기 위해 UICollectionViewFlowLayout 적용 --- ChatBot/ChatBot.xcodeproj/project.pbxproj | 4 +++ .../View/ChatMessageCollectionView.swift | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 ChatBot/ChatBot/View/ChatMessageCollectionView.swift diff --git a/ChatBot/ChatBot.xcodeproj/project.pbxproj b/ChatBot/ChatBot.xcodeproj/project.pbxproj index b4aed1ad..cf5a931d 100644 --- a/ChatBot/ChatBot.xcodeproj/project.pbxproj +++ b/ChatBot/ChatBot.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ E67855E82BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */; }; E67855EA2BC7B84400488352 /* MessageCellStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E92BC7B84400488352 /* MessageCellStackView.swift */; }; E67855EC2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855EB2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift */; }; + E67855EE2BC7BA9700488352 /* ChatMessageCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855ED2BC7BA9700488352 /* ChatMessageCollectionView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -48,6 +49,7 @@ E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailChatViewUserInputSectionStackView.swift; sourceTree = ""; }; E67855E92BC7B84400488352 /* MessageCellStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCellStackView.swift; sourceTree = ""; }; E67855EB2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailMessageCollectionViewCell.swift; sourceTree = ""; }; + E67855ED2BC7BA9700488352 /* ChatMessageCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessageCollectionView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -155,6 +157,7 @@ E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */, E67855E92BC7B84400488352 /* MessageCellStackView.swift */, E67855EB2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift */, + E67855ED2BC7BA9700488352 /* ChatMessageCollectionView.swift */, ); path = View; sourceTree = ""; @@ -229,6 +232,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E67855EE2BC7BA9700488352 /* ChatMessageCollectionView.swift in Sources */, B4B3E2C12B42D1BB00818B3C /* ChatbotMainViewController.swift in Sources */, 70A610432BBE6061009B08ED /* NetworkErrorEnum.swift in Sources */, 70E87ED22BBAA6F800E27E43 /* RequestDTO.swift in Sources */, diff --git a/ChatBot/ChatBot/View/ChatMessageCollectionView.swift b/ChatBot/ChatBot/View/ChatMessageCollectionView.swift new file mode 100644 index 00000000..be5971bc --- /dev/null +++ b/ChatBot/ChatBot/View/ChatMessageCollectionView.swift @@ -0,0 +1,33 @@ +// +// ChatMessageCollectionView.swift +// ChatBot +// +// Created by 권태호 on 4/11/24. +// + +import UIKit + +class ChatMessageCollectionView: UICollectionView { + init() { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + super.init(frame: .zero, collectionViewLayout: layout) + + self.register(MessageCollectionViewCell.self, forCellWithReuseIdentifier: MessageCollectionViewCell.identifier) + self.translatesAutoresizingMaskIntoConstraints = false + self.backgroundColor = .systemBackground + + layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize + layout.minimumLineSpacing = 5 + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + guard let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else { return } + flowLayout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) // 상, 하, 좌, 우 여백 설정 + } +} From f53ddf860ee78f9be28dee597d5d58bdf81c0eea Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 15:36:22 +0900 Subject: [PATCH 05/26] =?UTF-8?q?chore(*):=20CollectionViewCell=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChatBot/ChatBot/View/ChatMessageCollectionView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChatBot/ChatBot/View/ChatMessageCollectionView.swift b/ChatBot/ChatBot/View/ChatMessageCollectionView.swift index be5971bc..a9f68ff8 100644 --- a/ChatBot/ChatBot/View/ChatMessageCollectionView.swift +++ b/ChatBot/ChatBot/View/ChatMessageCollectionView.swift @@ -13,7 +13,7 @@ class ChatMessageCollectionView: UICollectionView { layout.scrollDirection = .vertical super.init(frame: .zero, collectionViewLayout: layout) - self.register(MessageCollectionViewCell.self, forCellWithReuseIdentifier: MessageCollectionViewCell.identifier) + self.register(DetailMessageCollectionViewCell.self, forCellWithReuseIdentifier: DetailMessageCollectionViewCell.identifier) self.translatesAutoresizingMaskIntoConstraints = false self.backgroundColor = .systemBackground From ba9ca40549cfe9a367506f8fb5e32d946c504337 Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 15:40:54 +0900 Subject: [PATCH 06/26] =?UTF-8?q?refactor(*):=20ViewController=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 명세서대로 ViewController 구현을 위해 API통신 TEST용 코드 삭제 --- .../ChatBot/ChatbotMainViewController.swift | 86 ------------------- 1 file changed, 86 deletions(-) diff --git a/ChatBot/ChatBot/ChatbotMainViewController.swift b/ChatBot/ChatBot/ChatbotMainViewController.swift index 87fed78f..c66ca150 100644 --- a/ChatBot/ChatBot/ChatbotMainViewController.swift +++ b/ChatBot/ChatBot/ChatbotMainViewController.swift @@ -25,93 +25,7 @@ final class ChatbotMainViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - repo = MessageRepository() - viewModel = ChatViewModel(repository: repo, apiService: apiService) - - setupSendMessageButton() - setupCheckStoregeButton() - setupclearRepoButton() - setupErrorAlert() - } - - - // MARK: - func - private func setupSendMessageButton() { - let sendButton = UIButton(type: .system) - sendButton.setTitle("Send Message", for: .normal) - sendButton.addTarget(self, action: #selector(sendMessage), for: .touchUpInside) - - sendButton.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(sendButton) - - NSLayoutConstraint.activate([ - sendButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), - sendButton.centerYAnchor.constraint(equalTo: view.centerYAnchor) - ]) - } - - private func setupCheckStoregeButton() { - let sendButton = UIButton(type: .system) - sendButton.setTitle("checkStorege", for: .normal) - sendButton.addTarget(self, action: #selector(printMessageRepositoryContents), for: .touchUpInside) - - sendButton.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(sendButton) - - NSLayoutConstraint.activate([ - sendButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), - sendButton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 40) - ]) - } - - private func setupclearRepoButton() { - let sendButton = UIButton(type: .system) - sendButton.setTitle("clearRepo", for: .normal) - sendButton.addTarget(self, action: #selector(messageClear), for: .touchUpInside) - - sendButton.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(sendButton) - - NSLayoutConstraint.activate([ - sendButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), - sendButton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 80) - ]) - } - - private func configuerErrorAlert(message: String) { - let alertController = UIAlertController(title: "Error발생", message: message, preferredStyle: .alert) - let action = UIAlertAction(title: "확인", style: .default) - alertController.addAction(action) - self.present(alertController, animated: true) - } - - private func setupErrorAlert() { - viewModel.onError = { [weak self] errorMessage in - DispatchQueue.main.async { - self?.configuerErrorAlert(message: errorMessage) - } - } - } - - // MARK: - objc func - @objc private func sendMessage() { - let message = "IOS 개발자가 되기 위한 구체적인 계획" - - DispatchQueue.main.async { - self.viewModel.processUserMessage(message: message, model: .gpt3Turbo) - } - } - @objc private func printMessageRepositoryContents() { - let messages = repo.getMessages() - print(""" - 😃😃😃😃😃😃😃😃😃😃😃😃😃😃😃😃😃 - \(messages) - 😃😃😃😃😃😃😃😃😃😃😃😃😃😃😃😃😃😃 - """) - } - @objc private func messageClear() { - repo.clearStorage() } } From 5f011f5ebc0de984e30fa62db5c94192be59a8a5 Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 15:56:16 +0900 Subject: [PATCH 07/26] =?UTF-8?q?feat(*):=20DetailChatViewController-UserI?= =?UTF-8?q?nputSection=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 유저 input section 부분 구현 및 autolayout적용 --- ChatBot/ChatBot.xcodeproj/project.pbxproj | 2 +- .../ChatBot/ChatbotMainViewController.swift | 31 --------- .../ChatBot/DetailChatViewController.swift | 66 +++++++++++++++++++ 3 files changed, 67 insertions(+), 32 deletions(-) delete mode 100644 ChatBot/ChatBot/ChatbotMainViewController.swift create mode 100644 ChatBot/ChatBot/DetailChatViewController.swift diff --git a/ChatBot/ChatBot.xcodeproj/project.pbxproj b/ChatBot/ChatBot.xcodeproj/project.pbxproj index cf5a931d..0b32bd45 100644 --- a/ChatBot/ChatBot.xcodeproj/project.pbxproj +++ b/ChatBot/ChatBot.xcodeproj/project.pbxproj @@ -138,13 +138,13 @@ B4B3E2BB2B42D1BB00818B3C /* ChatBot */ = { isa = PBXGroup; children = ( - E67855E62BC7B31700488352 /* View */, 70E483A52BB5AB6400931F01 /* Assets */, 70E483A42BB5AB5D00931F01 /* App */, 70E483AC2BB5AE7600931F01 /* ViewModel */, 70E483A32BB5AB3900931F01 /* Model */, 70E483A22BB5AB3700931F01 /* Repository */, 70E483A12BB5AB3400931F01 /* Network */, + E67855E62BC7B31700488352 /* View */, B4B3E2C02B42D1BB00818B3C /* ChatbotMainViewController.swift */, B4B3E2CA2B42D1BC00818B3C /* Info.plist */, ); diff --git a/ChatBot/ChatBot/ChatbotMainViewController.swift b/ChatBot/ChatBot/ChatbotMainViewController.swift deleted file mode 100644 index c66ca150..00000000 --- a/ChatBot/ChatBot/ChatbotMainViewController.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// ViewController.swift -// ChatBot -// -// Created by Tacocat on 1/1/24. -// - -import UIKit - -final class ChatbotMainViewController: UIViewController { - private var viewModel: ChatViewModel - private var repo: MessageRepository - private let apiService: OpenAIService - - init(viewModel: ChatViewModel, repo: MessageRepository, apiService: OpenAIService) { - self.apiService = apiService - self.viewModel = viewModel - self.repo = repo - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - repo = MessageRepository() - viewModel = ChatViewModel(repository: repo, apiService: apiService) - } -} diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift new file mode 100644 index 00000000..3376b460 --- /dev/null +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -0,0 +1,66 @@ +// +// ViewController.swift +// ChatBot +// +// Created by Tacocat on 1/1/24. +// + +import UIKit + +final class DetailChatViewController: UIViewController { + // MARK: - Property + + private var viewModel: ChatViewModel + private var repo: MessageRepository + private let apiService: OpenAIService + + private var detailChatStackView: UIStackView = DetailChatViewUserInputSectionStackView() + + + init(viewModel: ChatViewModel, repo: MessageRepository, apiService: OpenAIService) { + self.apiService = apiService + self.viewModel = viewModel + self.repo = repo + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + repo = MessageRepository() + viewModel = ChatViewModel(repository: repo, apiService: apiService) + } + + // MARK: - CustomFunc + private func configureDetailChatStackView() { + if let stackView = detailChatStackView as? DetailChatViewUserInputSectionStackView { + stackView.userInputTextView.delegate = self + } + self.view.addSubview(detailChatStackView) + detailChatStackView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + detailChatStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20), + detailChatStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20), + detailChatStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + detailChatStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20) + ]) + } + +} + +extension DetailChatViewController: UITextViewDelegate { + func textViewDidChange(_ textView: UITextView) { + let size = CGSize(width: view.frame.width - 40, height: .infinity) + let estimatedSize = textView.sizeThatFits(size) + let validHeight = estimatedSize.height.isNaN || estimatedSize.height < 0 ? 40 : estimatedSize.height + + textView.constraints.forEach { (constraint) in + if constraint.firstAttribute == .height { + constraint.constant = max(40, min(100, validHeight)) + } + } + } +} From fef9140fdb5f56e8f206a1bdfdfb9ed47ed9762e Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 16:47:29 +0900 Subject: [PATCH 08/26] =?UTF-8?q?feat(*):=20=ED=82=A4=EB=B3=B4=EB=93=9C?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=8F=99=EC=A0=81=20View?= =?UTF-8?q?=EC=9B=80=EC=A7=81=EC=9E=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit notification을 활용해서 키보드 호출에 따른 동적 View움직임 구현 --- ChatBot/ChatBot.xcodeproj/project.pbxproj | 8 ++-- ChatBot/ChatBot/App/SceneDelegate.swift | 2 +- .../ChatBot/DetailChatViewController.swift | 39 +++++++++++++++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/ChatBot/ChatBot.xcodeproj/project.pbxproj b/ChatBot/ChatBot.xcodeproj/project.pbxproj index 0b32bd45..0989ae24 100644 --- a/ChatBot/ChatBot.xcodeproj/project.pbxproj +++ b/ChatBot/ChatBot.xcodeproj/project.pbxproj @@ -19,7 +19,7 @@ 70E87ED22BBAA6F800E27E43 /* RequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70E87ED12BBAA6F800E27E43 /* RequestDTO.swift */; }; B4B3E2BD2B42D1BB00818B3C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3E2BC2B42D1BB00818B3C /* AppDelegate.swift */; }; B4B3E2BF2B42D1BB00818B3C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3E2BE2B42D1BB00818B3C /* SceneDelegate.swift */; }; - B4B3E2C12B42D1BB00818B3C /* ChatbotMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3E2C02B42D1BB00818B3C /* ChatbotMainViewController.swift */; }; + B4B3E2C12B42D1BB00818B3C /* DetailChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4B3E2C02B42D1BB00818B3C /* DetailChatViewController.swift */; }; B4B3E2C62B42D1BC00818B3C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B4B3E2C52B42D1BC00818B3C /* Assets.xcassets */; }; B4B3E2C92B42D1BC00818B3C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4B3E2C72B42D1BC00818B3C /* LaunchScreen.storyboard */; }; E67855E82BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */; }; @@ -42,7 +42,7 @@ B4B3E2B92B42D1BB00818B3C /* ChatBot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChatBot.app; sourceTree = BUILT_PRODUCTS_DIR; }; B4B3E2BC2B42D1BB00818B3C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; B4B3E2BE2B42D1BB00818B3C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - B4B3E2C02B42D1BB00818B3C /* ChatbotMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatbotMainViewController.swift; sourceTree = ""; }; + B4B3E2C02B42D1BB00818B3C /* DetailChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailChatViewController.swift; sourceTree = ""; }; B4B3E2C52B42D1BC00818B3C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B4B3E2C82B42D1BC00818B3C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; B4B3E2CA2B42D1BC00818B3C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -145,7 +145,7 @@ 70E483A22BB5AB3700931F01 /* Repository */, 70E483A12BB5AB3400931F01 /* Network */, E67855E62BC7B31700488352 /* View */, - B4B3E2C02B42D1BB00818B3C /* ChatbotMainViewController.swift */, + B4B3E2C02B42D1BB00818B3C /* DetailChatViewController.swift */, B4B3E2CA2B42D1BC00818B3C /* Info.plist */, ); path = ChatBot; @@ -233,7 +233,7 @@ buildActionMask = 2147483647; files = ( E67855EE2BC7BA9700488352 /* ChatMessageCollectionView.swift in Sources */, - B4B3E2C12B42D1BB00818B3C /* ChatbotMainViewController.swift in Sources */, + B4B3E2C12B42D1BB00818B3C /* DetailChatViewController.swift in Sources */, 70A610432BBE6061009B08ED /* NetworkErrorEnum.swift in Sources */, 70E87ED22BBAA6F800E27E43 /* RequestDTO.swift in Sources */, 70E4839C2BB5A88900931F01 /* RequestMessageModel.swift in Sources */, diff --git a/ChatBot/ChatBot/App/SceneDelegate.swift b/ChatBot/ChatBot/App/SceneDelegate.swift index 33a172ab..0090270d 100644 --- a/ChatBot/ChatBot/App/SceneDelegate.swift +++ b/ChatBot/ChatBot/App/SceneDelegate.swift @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let repo = MessageRepository() let apiService = OpenAIService() let viewModel = ChatViewModel(repository: repo, apiService: apiService) - let rootViewController = ChatbotMainViewController(viewModel: viewModel, repo: repo, apiService: apiService) + let rootViewController = DetailChatViewController(viewModel: viewModel, repo: repo, apiService: apiService) self.window?.rootViewController = rootViewController self.window?.makeKeyAndVisible() diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 3376b460..b9410968 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -30,8 +30,16 @@ final class DetailChatViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - repo = MessageRepository() - viewModel = ChatViewModel(repository: repo, apiService: apiService) + self.view.backgroundColor = .white + configureDetailChatStackView() + } + + override func viewWillAppear(_ animated: Bool) { + keyboardAppear() + } + + override func viewWillDisappear(_ animated: Bool) { + keyboardDisappear() } // MARK: - CustomFunc @@ -48,7 +56,32 @@ final class DetailChatViewController: UIViewController { detailChatStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20) ]) } - + // MARK: - keyBoardAction + @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) + } + ) + } + } + + @objc func keyboardDown() { + self.view.transform = .identity + } + + private func keyboardAppear() { + NotificationCenter.default.addObserver(self, selector: #selector(keyboardUp), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(keyboardDown), name: UIResponder.keyboardWillHideNotification, object: nil) + } + private func keyboardDisappear() { + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) + } + } extension DetailChatViewController: UITextViewDelegate { From a702ed45390cfbaafa748660faf458f91095ed18 Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 16:51:37 +0900 Subject: [PATCH 09/26] =?UTF-8?q?refactor(*):=20DetailChatViewUserInputSec?= =?UTF-8?q?tionStackView=20autolayout=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setupStackView메서드 내 textView Autolayout관련 코드 삭제 자동적으로 textView의 크기가 적용 될 수 있게 수정 --- .../View/DetailChatViewUserInputSectionStackView.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift b/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift index c0894471..77157559 100644 --- a/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift +++ b/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift @@ -52,10 +52,7 @@ class DetailChatViewUserInputSectionStackView: UIStackView { doneButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ doneButton.widthAnchor.constraint(equalToConstant: 40), - doneButton.heightAnchor.constraint(equalToConstant: 40), - doneButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10), - userInputTextView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10) - + doneButton.heightAnchor.constraint(equalToConstant: 40) ]) } From d7c7e24f45158a58ebf2445987cb4887430118b0 Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 17:04:09 +0900 Subject: [PATCH 10/26] =?UTF-8?q?feat(*):=20ChatMessageCollectionView=20Au?= =?UTF-8?q?tolayout=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatBot/DetailChatViewController.swift | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index b9410968..fc85b221 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -15,6 +15,7 @@ final class DetailChatViewController: UIViewController { private let apiService: OpenAIService private var detailChatStackView: UIStackView = DetailChatViewUserInputSectionStackView() + private var chatMessageCollectionView = ChatMessageCollectionView() init(viewModel: ChatViewModel, repo: MessageRepository, apiService: OpenAIService) { @@ -32,6 +33,7 @@ final class DetailChatViewController: UIViewController { super.viewDidLoad() self.view.backgroundColor = .white configureDetailChatStackView() + configureCollectionView() } override func viewWillAppear(_ animated: Bool) { @@ -56,6 +58,19 @@ final class DetailChatViewController: UIViewController { detailChatStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20) ]) } + + private func configureCollectionView() { + view.addSubview(chatMessageCollectionView) +// chatMessageCollectionView.dataSource = self + chatMessageCollectionView.delegate = self + + NSLayoutConstraint.activate([ + chatMessageCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10), + chatMessageCollectionView.leadingAnchor.constraint(equalTo: detailChatStackView.leadingAnchor), + chatMessageCollectionView.trailingAnchor.constraint(equalTo: detailChatStackView.trailingAnchor), + chatMessageCollectionView.bottomAnchor.constraint(equalTo: detailChatStackView.topAnchor, constant: -10) + ]) + } // MARK: - keyBoardAction @objc func keyboardUp(notification:NSNotification) { if let keyboardFrame:NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { @@ -97,3 +112,18 @@ extension DetailChatViewController: UITextViewDelegate { } } } + +// MARK: - CollectionView + +extension DetailChatViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 5 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + + } + + +} + From 575bd5f4a31fecc9149ec315c8f2635355c4f2c9 Mon Sep 17 00:00:00 2001 From: happykwon Date: Thu, 11 Apr 2024 17:07:43 +0900 Subject: [PATCH 11/26] =?UTF-8?q?feat(*):=20DetailMessageCollectionViewCel?= =?UTF-8?q?l=EC=97=90=20=EB=8F=99=EC=A0=81cell=EB=86=92=EC=9D=B4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 메시지 길이가 길어지는 것을 대비, 동적 높이 구현을 위한 로직 추가 --- ChatBot/ChatBot/DetailChatViewController.swift | 2 +- .../View/DetailMessageCollectionViewCell.swift | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index fc85b221..5e4e6789 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -61,7 +61,7 @@ final class DetailChatViewController: UIViewController { private func configureCollectionView() { view.addSubview(chatMessageCollectionView) -// chatMessageCollectionView.dataSource = self + chatMessageCollectionView.dataSource = self chatMessageCollectionView.delegate = self NSLayoutConstraint.activate([ diff --git a/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift b/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift index 3915cf5c..8a939463 100644 --- a/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift +++ b/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift @@ -27,6 +27,19 @@ class DetailMessageCollectionViewCell: UICollectionViewCell { fatalError("init(coder:) has not been implemented") } + //셀 높이 동적 제어 + override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { + super.preferredLayoutAttributesFitting(layoutAttributes) + layoutIfNeeded() + + let size = contentView.systemLayoutSizeFitting(layoutAttributes.size) + var newFrame = layoutAttributes.frame + newFrame.size.height = ceil(size.height) + layoutAttributes.frame = newFrame + + return layoutAttributes + } + private func setupMessageCollectionViewCell() { contentView.addSubview(messageCellStackView) messageCellStackView.translatesAutoresizingMaskIntoConstraints = false From f1b0e6400e7d49a1125fb16873f1709e5be16228 Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 10:50:36 +0900 Subject: [PATCH 12/26] =?UTF-8?q?chatView=20CollectionViewDelegate,DataSou?= =?UTF-8?q?rce=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CollectionView 셀구성 및 데이터 맵핑을 위한 프로토콜 구현 --- ChatBot/ChatBot/DetailChatViewController.swift | 17 +++++++++++------ ...etailChatViewUserInputSectionStackView.swift | 2 +- ChatBot/ChatBot/ViewModel/ChatViewModel.swift | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 5e4e6789..5d2233c4 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -115,15 +115,20 @@ extension DetailChatViewController: UITextViewDelegate { // MARK: - CollectionView -extension DetailChatViewController: UICollectionViewDelegate { +extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return 5 + return viewModel.messageRepository.getMessages().count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - - } - - + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailMessageCollectionViewCell.identifier, for: indexPath) as? DetailMessageCollectionViewCell else { + return UICollectionViewCell() + } + + let message = viewModel.messageRepository.getMessages()[indexPath.row] + cell.configureMessageCollectionViewCell(with: message) + + return cell + } } diff --git a/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift b/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift index 77157559..30e79ea9 100644 --- a/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift +++ b/ChatBot/ChatBot/View/DetailChatViewUserInputSectionStackView.swift @@ -17,7 +17,7 @@ class DetailChatViewUserInputSectionStackView: UIStackView { return textView }() - private var doneButton: UIButton = { + var doneButton: UIButton = { let button = UIButton() let image = UIImage(systemName: "arrowshape.up") button.setImage(image, for: .normal) diff --git a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift index c9d49933..5b112ff5 100644 --- a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift +++ b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift @@ -8,7 +8,7 @@ import Foundation final class ChatViewModel { - private let messageRepository: MessageRepository + let messageRepository: MessageRepository private let apiService: OpenAIService var onError:((String) -> Void)? From aa1695788912c406089f5dc8a5b48fe6a15b893a Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 10:55:37 +0900 Subject: [PATCH 13/26] =?UTF-8?q?MessageRepository=20Delegate=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChatBot/ChatBot/Repository/MessageRepository.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ChatBot/ChatBot/Repository/MessageRepository.swift b/ChatBot/ChatBot/Repository/MessageRepository.swift index 71a08441..14f4382d 100644 --- a/ChatBot/ChatBot/Repository/MessageRepository.swift +++ b/ChatBot/ChatBot/Repository/MessageRepository.swift @@ -7,13 +7,19 @@ import Foundation -class MessageRepository { +protocol MessageRepositoryDeletage: AnyObject { + func messageDidUpdate() +} + +class MessageRepository: MessageRepositoryDeletage { private var messagesStorage: [RequestMessageModel] = [] private let repoQueue = DispatchQueue(label: "repoQueue") + weak var delegate: MessageRepositoryDeletage? func addMessage(_ message: RequestMessageModel) { repoQueue.async { self.messagesStorage.append(message) + self.delegate?.messageDidUpdate() } } @@ -26,6 +32,7 @@ class MessageRepository { func clearStorage() { repoQueue.sync { self.messagesStorage.removeAll() + self.delegate?.messageDidUpdate() } } } From 992aecacfefe47de8d4029edbf18462de6f5cc75 Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 11:01:18 +0900 Subject: [PATCH 14/26] =?UTF-8?q?refactor(*):=20Delegate=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MessageRepository 객채 delegate 패턴 구현 구성 --- ChatBot/ChatBot/Repository/MessageRepository.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChatBot/ChatBot/Repository/MessageRepository.swift b/ChatBot/ChatBot/Repository/MessageRepository.swift index 14f4382d..1b65c622 100644 --- a/ChatBot/ChatBot/Repository/MessageRepository.swift +++ b/ChatBot/ChatBot/Repository/MessageRepository.swift @@ -11,7 +11,7 @@ protocol MessageRepositoryDeletage: AnyObject { func messageDidUpdate() } -class MessageRepository: MessageRepositoryDeletage { +class MessageRepository { private var messagesStorage: [RequestMessageModel] = [] private let repoQueue = DispatchQueue(label: "repoQueue") weak var delegate: MessageRepositoryDeletage? From 07c9df0beb426b26b65b13668ecb977bd696e142 Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 11:24:24 +0900 Subject: [PATCH 15/26] =?UTF-8?q?feat(*):=20UICollectionViewDelegateFlowLa?= =?UTF-8?q?yout=20=ED=94=84=EB=A1=9C=ED=86=A0=EC=BD=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit height를 동적으로 구하는 로직은 추후 추가 예정 --- ChatBot/ChatBot/DetailChatViewController.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 5d2233c4..927fe75e 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -44,7 +44,7 @@ final class DetailChatViewController: UIViewController { keyboardDisappear() } - // MARK: - CustomFunc + // MARK: - Autolayout private func configureDetailChatStackView() { if let stackView = detailChatStackView as? DetailChatViewUserInputSectionStackView { stackView.userInputTextView.delegate = self @@ -132,3 +132,11 @@ extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDa } } +extension DetailChatViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let message = viewModel.messageRepository.getMessages()[indexPath.row].content + + return CGSize(width: collectionView.frame.width, height: 100) + } +} + From 0d168c2e74012ec19a3c3f14e060fcebfc855e61 Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 11:44:14 +0900 Subject: [PATCH 16/26] =?UTF-8?q?feat(*):=20Button=ED=81=B4=EB=A6=AD=20?= =?UTF-8?q?=EC=8B=9C=20API=ED=86=B5=EC=8B=A0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit StackViewDoneButton 누를 시 API통신 구현 완료 및 테스트 완료 --- .../ChatBot/DetailChatViewController.swift | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 927fe75e..987e7b11 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -9,14 +9,14 @@ import UIKit final class DetailChatViewController: UIViewController { // MARK: - Property - + private var viewModel: ChatViewModel private var repo: MessageRepository private let apiService: OpenAIService - private var detailChatStackView: UIStackView = DetailChatViewUserInputSectionStackView() + private var detailChatStackView = DetailChatViewUserInputSectionStackView() private var chatMessageCollectionView = ChatMessageCollectionView() - + init(viewModel: ChatViewModel, repo: MessageRepository, apiService: OpenAIService) { self.apiService = apiService @@ -32,8 +32,9 @@ final class DetailChatViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .white + setupDetailChatStackView() + setupChatMessageCollectionView() configureDetailChatStackView() - configureCollectionView() } override func viewWillAppear(_ animated: Bool) { @@ -41,16 +42,15 @@ final class DetailChatViewController: UIViewController { } override func viewWillDisappear(_ animated: Bool) { - keyboardDisappear() + keyboardDisappear() } // MARK: - Autolayout - private func configureDetailChatStackView() { - if let stackView = detailChatStackView as? DetailChatViewUserInputSectionStackView { - stackView.userInputTextView.delegate = self - } + private func setupDetailChatStackView() { self.view.addSubview(detailChatStackView) detailChatStackView.translatesAutoresizingMaskIntoConstraints = false + detailChatStackView.userInputTextView.delegate = self + NSLayoutConstraint.activate([ detailChatStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20), detailChatStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20), @@ -59,10 +59,10 @@ final class DetailChatViewController: UIViewController { ]) } - private func configureCollectionView() { + private func setupChatMessageCollectionView() { view.addSubview(chatMessageCollectionView) - chatMessageCollectionView.dataSource = self - chatMessageCollectionView.delegate = self + chatMessageCollectionView.dataSource = self + chatMessageCollectionView.delegate = self NSLayoutConstraint.activate([ chatMessageCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10), @@ -71,10 +71,21 @@ final class DetailChatViewController: UIViewController { chatMessageCollectionView.bottomAnchor.constraint(equalTo: detailChatStackView.topAnchor, constant: -10) ]) } + + // MARK: - configure + private func configureDetailChatStackView() { + detailChatStackView.doneButton.addTarget(self, action: #selector(doneButtonTapped(_:)), for: .touchUpInside) + } + + + @objc private func doneButtonTapped(_ sender: UIButton) { + guard let userInput = detailChatStackView.userInputTextView.text, !userInput.isEmpty else { return } + viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) + } // MARK: - keyBoardAction @objc func keyboardUp(notification:NSNotification) { if let keyboardFrame:NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { - let keyboardRectangle = keyboardFrame.cgRectValue + let keyboardRectangle = keyboardFrame.cgRectValue UIView.animate( withDuration: 0.3 , animations: { @@ -121,15 +132,15 @@ extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDa } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailMessageCollectionViewCell.identifier, for: indexPath) as? DetailMessageCollectionViewCell else { - return UICollectionViewCell() - } - - let message = viewModel.messageRepository.getMessages()[indexPath.row] - cell.configureMessageCollectionViewCell(with: message) - - return cell - } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailMessageCollectionViewCell.identifier, for: indexPath) as? DetailMessageCollectionViewCell else { + return UICollectionViewCell() + } + + let message = viewModel.messageRepository.getMessages()[indexPath.row] + cell.configureMessageCollectionViewCell(with: message) + + return cell + } } extension DetailChatViewController: UICollectionViewDelegateFlowLayout { From bafe2b6de1ff4bd6b23479dd8d5ecf3a2a53833d Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 11:46:54 +0900 Subject: [PATCH 17/26] =?UTF-8?q?feat(*):=20doneButton=ED=81=B4=EB=A6=AD?= =?UTF-8?q?=20=EC=8B=9C=20=EA=B8=B0=EC=A1=B4=EB=82=B4=EC=9A=A9=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/xcschemes/ChatBot.xcscheme | 78 +++++++++++++++++++ .../ChatBot/DetailChatViewController.swift | 1 + 2 files changed, 79 insertions(+) create mode 100644 ChatBot/ChatBot.xcodeproj/xcshareddata/xcschemes/ChatBot.xcscheme diff --git a/ChatBot/ChatBot.xcodeproj/xcshareddata/xcschemes/ChatBot.xcscheme b/ChatBot/ChatBot.xcodeproj/xcshareddata/xcschemes/ChatBot.xcscheme new file mode 100644 index 00000000..5ac22da7 --- /dev/null +++ b/ChatBot/ChatBot.xcodeproj/xcshareddata/xcschemes/ChatBot.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 987e7b11..79177180 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -81,6 +81,7 @@ final class DetailChatViewController: UIViewController { @objc private func doneButtonTapped(_ sender: UIButton) { guard let userInput = detailChatStackView.userInputTextView.text, !userInput.isEmpty else { return } viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) + detailChatStackView.userInputTextView.text = "" } // MARK: - keyBoardAction @objc func keyboardUp(notification:NSNotification) { From 9ca2aa5b6152a5eff9f6812702367817916c06b6 Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 13:23:46 +0900 Subject: [PATCH 18/26] =?UTF-8?q?dataReload=EB=A5=BC=20=EC=9C=84=ED=95=9C?= =?UTF-8?q?=20bindViewModel=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scrollToButtom: 채팅에러처리를 위한 Alert구현, 최신 메시지 스크롤 로직 구현 --- .../ChatBot/DetailChatViewController.swift | 32 ++++++++++++++++++- ChatBot/ChatBot/ViewModel/ChatViewModel.swift | 4 ++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 79177180..d3eb2b7b 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -72,7 +72,7 @@ final class DetailChatViewController: UIViewController { ]) } - // MARK: - configure + // MARK: - configureButton private func configureDetailChatStackView() { detailChatStackView.doneButton.addTarget(self, action: #selector(doneButtonTapped(_:)), for: .touchUpInside) } @@ -83,6 +83,36 @@ final class DetailChatViewController: UIViewController { viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) detailChatStackView.userInputTextView.text = "" } + + // MARK: - configureViewModel + private func bindViewModel() { + viewModel.onMessagesUpdated = { [weak self] in + DispatchQueue.main.async { + self?.chatMessageCollectionView.reloadData() + self?.scrollToBottom() + } + } + viewModel.onError = { [weak self] errorMessage in + self?.configureErrorAlert() + } + } + + private func scrollToBottom() { + DispatchQueue.main.async { + if self.viewModel.messageRepository.getMessages().count > 0 { + let indexPath = IndexPath(item: self.viewModel.messageRepository.getMessages().count - 1, section: 0) + self.chatMessageCollectionView.scrollToItem(at: indexPath, at: .bottom, animated: true) + } + } + } + + private func configureErrorAlert() { + let alert = UIAlertController(title: "Error", message: "관리자에게 문의해주세요", preferredStyle: .alert) + let action = UIAlertAction(title: "확인", style: .default) + alert.addAction(action) + present(alert, animated: true) + } + // MARK: - keyBoardAction @objc func keyboardUp(notification:NSNotification) { if let keyboardFrame:NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { diff --git a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift index 5b112ff5..0e22914d 100644 --- a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift +++ b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift @@ -12,6 +12,7 @@ final class ChatViewModel { private let apiService: OpenAIService var onError:((String) -> Void)? + var onMessagesUpdated: (() -> Void)? init(repository: MessageRepository, apiService: OpenAIService) { self.messageRepository = repository @@ -29,8 +30,9 @@ final class ChatViewModel { receivedMessages.forEach { responseMessage in self?.messageRepository.addMessage(responseMessage) } + self?.onMessagesUpdated?() case .failure(let error): - self?.onError?("Error 발생: 관라자에게 문의해주세요 \(error.localizedDescription)") + self?.onError?(error.localizedDescription) } } } From a0b5d6bcd4615626468dab9bcd32727aba2d621f Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 14:23:01 +0900 Subject: [PATCH 19/26] =?UTF-8?q?refactor(*):=20cell=20autolayout=EC=9E=AC?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 동작 로직에 상관없는 코드 삭제 --- ChatBot/ChatBot/DetailChatViewController.swift | 1 + ChatBot/ChatBot/View/MessageCellStackView.swift | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index d3eb2b7b..7196a9a7 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -35,6 +35,7 @@ final class DetailChatViewController: UIViewController { setupDetailChatStackView() setupChatMessageCollectionView() configureDetailChatStackView() + bindViewModel() } override func viewWillAppear(_ animated: Bool) { diff --git a/ChatBot/ChatBot/View/MessageCellStackView.swift b/ChatBot/ChatBot/View/MessageCellStackView.swift index 254bbebe..015aa829 100644 --- a/ChatBot/ChatBot/View/MessageCellStackView.swift +++ b/ChatBot/ChatBot/View/MessageCellStackView.swift @@ -44,13 +44,8 @@ class MessageCellStackView: UIStackView { let message = content.content contentLabel.text = message contentLabel.backgroundColor = content.role == .user ? .systemBlue : .systemGray - contentLabel.textColor = content.role == .user ? .black : .black + contentLabel.textColor = .black contentLabel.textAlignment = .left senderLabel.text = content.role == .user ? "😁You" : "🤖Bot" } - - func reset() { - contentLabel.text = nil - senderLabel.text = nil - } } From 150001daaea917aaf21a3e9e8f4137eba53565ec Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 14:23:42 +0900 Subject: [PATCH 20/26] =?UTF-8?q?MessageCellStackView=20backgroundColor=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift | 5 ++--- ChatBot/ChatBot/View/MessageCellStackView.swift | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift b/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift index 8a939463..28de4c2f 100644 --- a/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift +++ b/ChatBot/ChatBot/View/DetailMessageCollectionViewCell.swift @@ -20,7 +20,6 @@ class DetailMessageCollectionViewCell: UICollectionViewCell { override func prepareForReuse() { super.prepareForReuse() - messageCellStackView.reset() } required init?(coder: NSCoder) { @@ -46,8 +45,8 @@ class DetailMessageCollectionViewCell: UICollectionViewCell { NSLayoutConstraint.activate([ messageCellStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), messageCellStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10), - messageCellStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), - messageCellStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10) + messageCellStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: -5), + messageCellStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15) ]) } diff --git a/ChatBot/ChatBot/View/MessageCellStackView.swift b/ChatBot/ChatBot/View/MessageCellStackView.swift index 015aa829..389a009f 100644 --- a/ChatBot/ChatBot/View/MessageCellStackView.swift +++ b/ChatBot/ChatBot/View/MessageCellStackView.swift @@ -43,7 +43,6 @@ class MessageCellStackView: UIStackView { func configureStackView(with content: RequestMessageModel) { let message = content.content contentLabel.text = message - contentLabel.backgroundColor = content.role == .user ? .systemBlue : .systemGray contentLabel.textColor = .black contentLabel.textAlignment = .left senderLabel.text = content.role == .user ? "😁You" : "🤖Bot" From 36201a52540b41093513711652ebebce07d90024 Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 15:56:38 +0900 Subject: [PATCH 21/26] =?UTF-8?q?refactor(*):=20CollectionViewCell=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20=EB=86=92=EC=9D=B4=20=ED=95=A0=EB=8B=B9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=B4=20UICollectionViewDelegateFlowLa?= =?UTF-8?q?yout=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit greatestFiniteMagnitude를 사용해 height 길이 설정 및 boundingRect메서드를 사용해 높이 구함 --- ChatBot/ChatBot/DetailChatViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 7196a9a7..063cfb73 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -178,8 +178,10 @@ extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDa extension DetailChatViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let message = viewModel.messageRepository.getMessages()[indexPath.row].content - - return CGSize(width: collectionView.frame.width, height: 100) + let width = collectionView.frame.width - 20 + let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) + let attributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)] + let boundingRectSize = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) + return CGSize(width: width, height: ceil(boundingRectSize.height)) } } - From c209916d3c0355de45bba01d7ffeda0c5e718476 Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 16:14:36 +0900 Subject: [PATCH 22/26] =?UTF-8?q?refactor(*):=20autolayout=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit autolayout충돌 로그를 확인하고 CGSize_height 값 조정 --- ChatBot/ChatBot/DetailChatViewController.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 063cfb73..77011dea 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -177,11 +177,12 @@ extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDa extension DetailChatViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - let message = viewModel.messageRepository.getMessages()[indexPath.row].content + +// let message = viewModel.messageRepository.getMessages()[indexPath.row].content let width = collectionView.frame.width - 20 - let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) - let attributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)] - let boundingRectSize = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) - return CGSize(width: width, height: ceil(boundingRectSize.height)) +// let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) +// let attributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)] +// let boundingRectSize = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) + return CGSize(width: width, height: 30000) } } From 73e01086cade0e964f818d892991e1796bcdd46a Mon Sep 17 00:00:00 2001 From: happykwon Date: Fri, 12 Apr 2024 22:10:34 +0900 Subject: [PATCH 23/26] =?UTF-8?q?feat(*):=20API=ED=86=B5=EC=8B=A0=20Indica?= =?UTF-8?q?tor=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChatBot/DetailChatViewController.swift | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/DetailChatViewController.swift index 77011dea..cb5f10fc 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/DetailChatViewController.swift @@ -13,6 +13,7 @@ final class DetailChatViewController: UIViewController { private var viewModel: ChatViewModel private var repo: MessageRepository private let apiService: OpenAIService + private var openAIAPIResponseIndicatorView: UIActivityIndicatorView? private var detailChatStackView = DetailChatViewUserInputSectionStackView() private var chatMessageCollectionView = ChatMessageCollectionView() @@ -72,6 +73,19 @@ final class DetailChatViewController: UIViewController { chatMessageCollectionView.bottomAnchor.constraint(equalTo: detailChatStackView.topAnchor, constant: -10) ]) } + // MARK: - Indicator + private func configureopenAIAPIResponseIndicator() { + openAIAPIResponseIndicatorView = UIActivityIndicatorView(style: .large) + openAIAPIResponseIndicatorView?.center = self.view.center + self.view.addSubview(openAIAPIResponseIndicatorView!) + openAIAPIResponseIndicatorView?.startAnimating() + } + + private func hideOpenAIAPIResponseIndicator() { + openAIAPIResponseIndicatorView?.stopAnimating() + openAIAPIResponseIndicatorView?.removeFromSuperview() + } + // MARK: - configureButton private func configureDetailChatStackView() { @@ -80,9 +94,12 @@ final class DetailChatViewController: UIViewController { @objc private func doneButtonTapped(_ sender: UIButton) { - guard let userInput = detailChatStackView.userInputTextView.text, !userInput.isEmpty else { return } - viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) + guard let userInput = detailChatStackView.userInputTextView.text, !userInput.isEmpty else { + return //유저 알럿 띄우기 + } detailChatStackView.userInputTextView.text = "" + configureopenAIAPIResponseIndicator() + viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) } // MARK: - configureViewModel @@ -91,10 +108,12 @@ final class DetailChatViewController: UIViewController { DispatchQueue.main.async { self?.chatMessageCollectionView.reloadData() self?.scrollToBottom() + self?.hideOpenAIAPIResponseIndicator() } } viewModel.onError = { [weak self] errorMessage in self?.configureErrorAlert() + self?.hideOpenAIAPIResponseIndicator() } } @@ -106,14 +125,13 @@ final class DetailChatViewController: UIViewController { } } } - + // MARK: - Alert Configure private func configureErrorAlert() { let alert = UIAlertController(title: "Error", message: "관리자에게 문의해주세요", preferredStyle: .alert) let action = UIAlertAction(title: "확인", style: .default) alert.addAction(action) present(alert, animated: true) } - // MARK: - keyBoardAction @objc func keyboardUp(notification:NSNotification) { if let keyboardFrame:NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { From 985e2f0a31535a35a1a9d0b9b30118002dfa3e2f Mon Sep 17 00:00:00 2001 From: happykwon Date: Sun, 14 Apr 2024 23:51:00 +0900 Subject: [PATCH 24/26] =?UTF-8?q?indicator=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChatBot/ChatBot.xcodeproj/project.pbxproj | 2 +- .../{ => View}/DetailChatViewController.swift | 51 ++++++++++--------- ChatBot/ChatBot/ViewModel/ChatViewModel.swift | 11 +++- 3 files changed, 38 insertions(+), 26 deletions(-) rename ChatBot/ChatBot/{ => View}/DetailChatViewController.swift (85%) diff --git a/ChatBot/ChatBot.xcodeproj/project.pbxproj b/ChatBot/ChatBot.xcodeproj/project.pbxproj index 0989ae24..a7bfa90e 100644 --- a/ChatBot/ChatBot.xcodeproj/project.pbxproj +++ b/ChatBot/ChatBot.xcodeproj/project.pbxproj @@ -145,7 +145,6 @@ 70E483A22BB5AB3700931F01 /* Repository */, 70E483A12BB5AB3400931F01 /* Network */, E67855E62BC7B31700488352 /* View */, - B4B3E2C02B42D1BB00818B3C /* DetailChatViewController.swift */, B4B3E2CA2B42D1BC00818B3C /* Info.plist */, ); path = ChatBot; @@ -154,6 +153,7 @@ E67855E62BC7B31700488352 /* View */ = { isa = PBXGroup; children = ( + B4B3E2C02B42D1BB00818B3C /* DetailChatViewController.swift */, E67855E72BC7B35500488352 /* DetailChatViewUserInputSectionStackView.swift */, E67855E92BC7B84400488352 /* MessageCellStackView.swift */, E67855EB2BC7B9AB00488352 /* DetailMessageCollectionViewCell.swift */, diff --git a/ChatBot/ChatBot/DetailChatViewController.swift b/ChatBot/ChatBot/View/DetailChatViewController.swift similarity index 85% rename from ChatBot/ChatBot/DetailChatViewController.swift rename to ChatBot/ChatBot/View/DetailChatViewController.swift index cb5f10fc..d6e4cba0 100644 --- a/ChatBot/ChatBot/DetailChatViewController.swift +++ b/ChatBot/ChatBot/View/DetailChatViewController.swift @@ -13,7 +13,11 @@ final class DetailChatViewController: UIViewController { private var viewModel: ChatViewModel private var repo: MessageRepository private let apiService: OpenAIService - private var openAIAPIResponseIndicatorView: UIActivityIndicatorView? + private lazy var openAIAPIResponseIndicatorView: UIActivityIndicatorView = { + let indicator = UIActivityIndicatorView(style: .medium) + indicator.center = self.view.center + return indicator + }() private var detailChatStackView = DetailChatViewUserInputSectionStackView() private var chatMessageCollectionView = ChatMessageCollectionView() @@ -74,32 +78,32 @@ final class DetailChatViewController: UIViewController { ]) } // MARK: - Indicator - private func configureopenAIAPIResponseIndicator() { - openAIAPIResponseIndicatorView = UIActivityIndicatorView(style: .large) - openAIAPIResponseIndicatorView?.center = self.view.center - self.view.addSubview(openAIAPIResponseIndicatorView!) - openAIAPIResponseIndicatorView?.startAnimating() + private func showIndicator() { + self.view.addSubview(openAIAPIResponseIndicatorView) + openAIAPIResponseIndicatorView.startAnimating() } - private func hideOpenAIAPIResponseIndicator() { - openAIAPIResponseIndicatorView?.stopAnimating() - openAIAPIResponseIndicatorView?.removeFromSuperview() + func hideOpenAIAPIResponseIndicator() { + openAIAPIResponseIndicatorView.stopAnimating() + openAIAPIResponseIndicatorView.removeFromSuperview() } - + // MARK: - configureButton private func configureDetailChatStackView() { detailChatStackView.doneButton.addTarget(self, action: #selector(doneButtonTapped(_:)), for: .touchUpInside) } - + @objc private func doneButtonTapped(_ sender: UIButton) { guard let userInput = detailChatStackView.userInputTextView.text, !userInput.isEmpty else { - return //유저 알럿 띄우기 + return } detailChatStackView.userInputTextView.text = "" - configureopenAIAPIResponseIndicator() - viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) + showIndicator() + viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) { [weak self] in + self?.hideOpenAIAPIResponseIndicator() + } } // MARK: - configureViewModel @@ -108,13 +112,14 @@ final class DetailChatViewController: UIViewController { DispatchQueue.main.async { self?.chatMessageCollectionView.reloadData() self?.scrollToBottom() - self?.hideOpenAIAPIResponseIndicator() } } - viewModel.onError = { [weak self] errorMessage in - self?.configureErrorAlert() - self?.hideOpenAIAPIResponseIndicator() - } + + DispatchQueue.main.async { + self.viewModel.onError = { [weak self] errorMessage in + self?.configureErrorAlert() + } + } } private func scrollToBottom() { @@ -196,11 +201,11 @@ extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDa extension DetailChatViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { -// let message = viewModel.messageRepository.getMessages()[indexPath.row].content + // let message = viewModel.messageRepository.getMessages()[indexPath.row].content let width = collectionView.frame.width - 20 -// let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) -// let attributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)] -// let boundingRectSize = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) + // let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) + // let attributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)] + // let boundingRectSize = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) return CGSize(width: width, height: 30000) } } diff --git a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift index 0e22914d..a514951e 100644 --- a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift +++ b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift @@ -19,11 +19,17 @@ final class ChatViewModel { self.apiService = apiService } - func processUserMessage(message content: String, model: GPTModel) { + func processUserMessage(message content: String, model: GPTModel, completion: @escaping () -> Void) { let userMessage = RequestMessageModel(role: .user, content: content) messageRepository.addMessage(userMessage) - apiService.sendRequestToOpenAI(messageRepository.getMessages(), model: model, APIkey: APIKeyManager.openAIAPIKey) { [weak self] result in + DispatchQueue.main.async { [weak self] in + self?.onMessagesUpdated?() + } + + apiService.sendRequestToOpenAI(messageRepository.getMessages(), + model: model, + APIkey: APIKeyManager.openAIAPIKey) { [weak self] result in DispatchQueue.main.async { switch result { case .success(let receivedMessages): @@ -34,6 +40,7 @@ final class ChatViewModel { case .failure(let error): self?.onError?(error.localizedDescription) } + completion() } } } From c9b4b05dc56d8b297c230aac7c8f157a0efbc32f Mon Sep 17 00:00:00 2001 From: happykwon Date: Mon, 15 Apr 2024 00:34:20 +0900 Subject: [PATCH 25/26] =?UTF-8?q?CollectionViewCell=ED=81=AC=EA=B8=B0=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20=EC=A1=B0=EC=A0=88=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=8F=20viewModel=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 수정사항: viewModel내 UI구현 로직(dispatchQueue)삭제, viewController에 추가 --- .../View/DetailChatViewController.swift | 33 +++++++++++-------- ChatBot/ChatBot/ViewModel/ChatViewModel.swift | 24 ++++++-------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/ChatBot/ChatBot/View/DetailChatViewController.swift b/ChatBot/ChatBot/View/DetailChatViewController.swift index d6e4cba0..44b40efd 100644 --- a/ChatBot/ChatBot/View/DetailChatViewController.swift +++ b/ChatBot/ChatBot/View/DetailChatViewController.swift @@ -78,12 +78,12 @@ final class DetailChatViewController: UIViewController { ]) } // MARK: - Indicator - private func showIndicator() { + private func showOpenAIAPIResponseIndicator() { self.view.addSubview(openAIAPIResponseIndicatorView) openAIAPIResponseIndicatorView.startAnimating() } - func hideOpenAIAPIResponseIndicator() { + private func hideOpenAIAPIResponseIndicator() { openAIAPIResponseIndicatorView.stopAnimating() openAIAPIResponseIndicatorView.removeFromSuperview() } @@ -100,9 +100,15 @@ final class DetailChatViewController: UIViewController { return } detailChatStackView.userInputTextView.text = "" - showIndicator() + + DispatchQueue.main.async { + self.showOpenAIAPIResponseIndicator() + } + viewModel.processUserMessage(message: userInput, model: .gpt3Turbo) { [weak self] in - self?.hideOpenAIAPIResponseIndicator() + DispatchQueue.main.async { + self?.hideOpenAIAPIResponseIndicator() + } } } @@ -117,7 +123,9 @@ final class DetailChatViewController: UIViewController { DispatchQueue.main.async { self.viewModel.onError = { [weak self] errorMessage in - self?.configureErrorAlert() + DispatchQueue.main.async { + self?.configureErrorAlert() + } } } } @@ -200,12 +208,11 @@ extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDa extension DetailChatViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - - // let message = viewModel.messageRepository.getMessages()[indexPath.row].content - let width = collectionView.frame.width - 20 - // let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) - // let attributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)] - // let boundingRectSize = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) - return CGSize(width: width, height: 30000) - } + let message = viewModel.messageRepository.getMessages()[indexPath.row].content + let width = collectionView.frame.width - 40 + let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) + let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)] + let estimatedFrame = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) + return CGSize(width: width, height: estimatedFrame.height + 20) + } } diff --git a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift index a514951e..6fc64dde 100644 --- a/ChatBot/ChatBot/ViewModel/ChatViewModel.swift +++ b/ChatBot/ChatBot/ViewModel/ChatViewModel.swift @@ -22,26 +22,22 @@ final class ChatViewModel { func processUserMessage(message content: String, model: GPTModel, completion: @escaping () -> Void) { let userMessage = RequestMessageModel(role: .user, content: content) messageRepository.addMessage(userMessage) - - DispatchQueue.main.async { [weak self] in - self?.onMessagesUpdated?() - } + self.onMessagesUpdated?() apiService.sendRequestToOpenAI(messageRepository.getMessages(), model: model, APIkey: APIKeyManager.openAIAPIKey) { [weak self] result in - DispatchQueue.main.async { - switch result { - case .success(let receivedMessages): - receivedMessages.forEach { responseMessage in - self?.messageRepository.addMessage(responseMessage) - } - self?.onMessagesUpdated?() - case .failure(let error): - self?.onError?(error.localizedDescription) + switch result { + case .success(let receivedMessages): + receivedMessages.forEach { responseMessage in + self?.messageRepository.addMessage(responseMessage) } - completion() + self?.onMessagesUpdated?() + case .failure(let error): + self?.onError?(error.localizedDescription) } + completion() } } } + From 700f95c901c4adc7ea1f1db2a3705c72a7e70f21 Mon Sep 17 00:00:00 2001 From: happykwon Date: Mon, 15 Apr 2024 13:14:35 +0900 Subject: [PATCH 26/26] =?UTF-8?q?refactor(*):=20UICollectionViewDelegateFl?= =?UTF-8?q?owLayout=20width=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChatBot/ChatBot/View/DetailChatViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChatBot/ChatBot/View/DetailChatViewController.swift b/ChatBot/ChatBot/View/DetailChatViewController.swift index 44b40efd..1168b0b0 100644 --- a/ChatBot/ChatBot/View/DetailChatViewController.swift +++ b/ChatBot/ChatBot/View/DetailChatViewController.swift @@ -209,7 +209,7 @@ extension DetailChatViewController: UICollectionViewDelegate, UICollectionViewDa extension DetailChatViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let message = viewModel.messageRepository.getMessages()[indexPath.row].content - let width = collectionView.frame.width - 40 + let width = collectionView.frame.width let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)] let estimatedFrame = NSString(string: message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)