Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Protocol
- UITextView
- Refactoring
- swift documentation
- combine
- 리펙토링
- 스위프트
- Xcode
- SWIFT
- MVVM
- Observable
- swiftUI
- uiscrollview
- uitableview
- Clean Code
- 애니메이션
- map
- RxCocoa
- rxswift
- collectionview
- tableView
- UICollectionView
- ribs
- clean architecture
- HIG
- 클린 코드
- Human interface guide
- 리팩토링
- 리펙터링
- ios
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. 실시간 채팅 앱 구현 방법 (MessageKit, Firebase, Firestore) - 채팅 UI 본문
iOS 응용 (swift)
[iOS - swift] 1. 실시간 채팅 앱 구현 방법 (MessageKit, Firebase, Firestore) - 채팅 UI
jake-kim 2021. 11. 16. 03:351. 실시간 채팅 앱 구현 방법 (MessageKit, Firebase, Firestore) - 채팅 UI
2. 실시간 채팅 앱 구현 방법 (MessageKit, Firebase, Firestore) - Firebase
SPM으로 MessageKit 설치
https://github.com/MessageKit/MessageKit
화면 플로우
MessageKit 주요 모듈
- MessagesViewController: UICollectionView를 상속하여 구현된 모듈이고 이 모듈을 상속하여 CahtViewController 생성
- 내부적으로 messagesCollectionView 프로퍼티 사용 가능
import UIKit
import MessageKit
class ChatVC: MessagesViewController {
}
- MessageKit SDK를 다운받으면 AccessoryView위에 붙어있는 InputBar도 사용할 수 있으므로, InputBarAccessoryView도 import
- 내부적으로 messageInputBar 사용 가능
import InputBarAccessoryView
messagesCollectionView 델리게이트 3가지
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
- MessagesDataSource
- MessageLayoutDelegate
- MessageDisplayDelegete
messageInputBar 델리게이트
- InputBarAccessoryViewDelegate
구현 - 데이터 모델 정의
- 채팅방에 들어가기 전에 해당 채팅방에 들어갈 때, 해당 채팅방 정보를 담을 Channel 모델 정의
// Channel.swift
struct Channel {
var id: String?
let name: String
init(id: String? = nil, name: String) {
self.id = id
self.name = name
}
}
extension Channel: Comparable {
static func == (lhs: Channel, rhs: Channel) -> Bool {
return lhs.id == rhs.id
}
static func < (lhs: Channel, rhs: Channel) -> Bool {
return lhs.name < rhs.name
}
}
- 채팅 내용에 이미지가 들어갈 수 있으므로, 이미지 파일과 연관된 모델 정의
- MediaItem은 MessageKit안에 존재하는 protocol이며 이미지를 표출하는데 필요한 필수 정보들을 정의
// ImageMediaItem.swift
import UIKit
import MessageKit
struct ImageMediaItem: MediaItem {
var url: URL?
var image: UIImage?
var placeholderImage: UIImage
var size: CGSize
init(image: UIImage) {
self.image = image
self.size = CGSize(width: 240, height: 240)
self.placeholderImage = UIImage()
}
}
- 대화 내용에 사용될 Message 모델 정의
- MessageType은 MessageKit내부에 존재하는 protocol이며 message의 필수 정보들을 정의
// Message.swift
import Foundation
import MessageKit
import UIKit
struct Message: MessageType {
let id: String?
var messageId: String {
return id ?? UUID().uuidString
}
let content: String
let sentDate: Date
let sender: SenderType
var kind: MessageKind {
if let image = image {
let mediaItem = ImageMediaItem(image: image)
return .photo(mediaItem)
} else {
return .text(content)
}
}
var image: UIImage?
var downloadURL: URL?
init(content: String) {
sender = Sender(senderId: "id(TODO...)", displayName: "displayName(TODO...)")
self.content = content
sentDate = Date()
id = nil
}
init(image: UIImage) {
sender = Sender(senderId: "id(TODO...)", displayName: "displayName(TODO...)")
self.image = image
sentDate = Date()
content = ""
id = nil
}
}
extension Message: Comparable {
static func == (lhs: Message, rhs: Message) -> Bool {
return lhs.id == rhs.id
}
static func < (lhs: Message, rhs: Message) -> Bool {
return lhs.sentDate < rhs.sentDate
}
}
채팅 UI 구현 핵심 코드
- import
import UIKit
import MessageKit
import InputBarAccessoryView
- MessagesViewController 상속
class ChatVC: MessagesViewController {
}
- 현재 자기 자신의 프로필 정보 및 collectionView에 표출 될 dataSource 선언
let channel: Channel
var sender = Sender(senderId: "any_unique_id", displayName: "jake")
var messages = [Message]()
- delegate 선정
private func confirmDelegates() {
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
messageInputBar.delegate = self
}
- send 버튼이 눌려지면 메시지를 collectionView의 cell에 표출하는 코드
private func insertNewMessage(_ message: Message) {
messages.append(message)
messages.sort()
messagesCollectionView.reloadData()
}
- Delegate 구현 - MessageDataSource (메시지 데이터 정의)
extension ChatVC: MessagesDataSource {
func currentSender() -> SenderType {
return sender
}
func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int {
return messages.count
}
func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType {
return messages[indexPath.section]
}
func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
let name = message.sender.displayName
return NSAttributedString(string: name, attributes: [.font: UIFont.preferredFont(forTextStyle: .caption1),
.foregroundColor: UIColor(white: 0.3, alpha: 1)])
}
}
- Delegate 구현 - MessageLayoutDelegate (셀 관련 높이 값)
extension ChatVC: MessagesLayoutDelegate {
// 아래 여백
func footerViewSize(for section: Int, in messagesCollectionView: MessagesCollectionView) -> CGSize {
return CGSize(width: 0, height: 8)
}
// 말풍선 위 이름 나오는 곳의 height
func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
return 20
}
}
- Delegate 구현 - MessagesDisplayDelegate (상대방이 보낸 메시지, 내가 보낸 메시지를 구분하여 색상과 모양 지정)
- cf) 상대방이 보낸거면 좌측, 내가 보낸거면 우측에 말풍선을 배치하는 것은 내부적으로 구현되어 있음
extension ChatVC: MessagesDisplayDelegate {
// 말풍선의 배경 색상
func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
return isFromCurrentSender(message: message) ? .primary : .incomingMessageBackground
}
func textColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
return isFromCurrentSender(message: message) ? .black : .white
}
// 말풍선의 꼬리 모양 방향
func messageStyle(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageStyle {
let cornerDirection: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft
return .bubbleTail(cornerDirection, .curved)
}
}
- Delegate 구현 - InputBarAccessoryViewDelegate (검색창에서 send 버튼을 누를 경우 이벤트 처리)
extension ChatVC: InputBarAccessoryViewDelegate {
func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) {
let message = Message(content: text)
insertNewMessage(message)
inputBar.inputTextView.text.removeAll()
}
}
* 전체 소스 코드: https://github.com/JK0369/ExChatWithRealTime/tree/Implement-Chat-UI
* 참고
https://www.raywenderlich.com/22067733-firebase-tutorial-real-time-chat
https://github.com/MessageKit/MessageKit/blob/master/Documentation/QuickStart.md
'iOS 응용 (swift)' 카테고리의 다른 글
Comments