SwiftUI로 채팅UI 화면 만들기

 
 
 
Introduction

  이번 포스팅은 기기에서 테스트를 하며 개발을 해야합니다. 그 이유는 물리적인 키보드를 이용하기 때문인데, 해당 인터페이스를 개발하고나서 직접 사용하려보면 키보드가 화면을 가려 입력되는 화면을 볼 수 없기 때문입니다. 간혹가다가 그런 어플들이 있지 않던가요? 이번 포스팅은 단순히 채팅UI만을 구성하지만, 키보드입력까지는 고려하지 않았습니다. 키보드입력관련해서는 필요에 따라 추가 포스팅을 할테니 추가자료를 검토해주시기 바랍니다. 그럼 천천히 채팅UI를 만들어 볼까요? 
 
SwiftUI로 채팅UI 화면 만들기 프롤로그

 
 
Balsamiq Mockups을 이용해서 간략하게 만들었는데, 복잡해보이네요…그래도 하나씩 정리를 해봅시다.!
 
#무엇이 필요할까?
  • Image
  • Text Field
  • Text
  • Icon
  • NavigationBar / Title 
 
이번에도 navigationBar 를 제외하고 앞서 포스팅한 내용과 크게 다르지 않습니다. 먼저 결과물과 레이아웃을 확인해보도록 할까요?

 

 

 
SwiftUI기반 채팅인터페이스 만들기

 
 
앞서 포스팅한 친구목록,채팅목록과 크게 다르지 않습니다. 약간의 응용이 있을뿐이죠. 그쵸? 레이아웃이 너무 많아서 해깔릴려나요? 일부분을 확대해서 먼저 보도록 하겠습니다. 
 
 
화면을 보면, 사용자이미지,이름을 VStack으로 구성하면 되겠죠? 그리고 좌측 혹은 우측 방향으로 사용자가 입력한 채팅내용을 추가하면 됩니다. 생각보다 간단하죠? 네, 간단합니다. ㅎㅎ
 
여기서 몇가지 확인하고 넘어가야하는 부분은 사용자가 입력하는 글자의 길이에 따라 가시화를 어떻게 할껀지에 대해 의문을 가져야 합니다. 현재 사용하는 카X에서도 글자의 길이에 따라 동적으로 변하자나요? 이부분도 간단히 해결가능하지만, 지금은 전체적인 UI를 구성해보도록 하겠습니다. 
 
 
채팅 Log Sample Data 추가

 
이번에도 Sample Data를 작성하기위해 TalkLog.swift를 추가합니다. 그리고 다음의 코드를 작성하도록 하겠습니다. 
 
TalkLog.swift
enum TalkDirection: Int, Codable {
    case left
    case right
}

enum TalkMsgType: Int, Codable {
    case message
    case image
    case webLink
    case emotion
}

struct TalkLog: Codable, Identifiable {
    let id:UUID = UUID()
    
    var Name:String
    var Message:String
    var Direction:TalkDirection
    var type:TalkMsgType
    
    init(_ name:String, _ message:String, _ direction:TalkDirection, _ type:TalkMsgType){
        self.Name      = name
        self.Message    = message
        self.Direction  = direction
        self.type      = type
    }
}
 
코드에 열거체를 포함시켰는데, 인터페이스와 상관없는 부분이기때문에 자신의 코드 스타일에 맞춰서 작성하시면 됩니다. 
 
Sample code
let TalkSamples = [
    TalkLog("박민호", "안녕하세요",TalkDirection.left, TalkMsgType.message),
    TalkLog("노은지", "안녕하세요",TalkDirection.right, TalkMsgType.message),
    
    TalkLog("박민호", "SwiftUI 한잔 어때요?",TalkDirection.left, TalkMsgType.message),
    TalkLog("노은지", "네, 좋아요 ",TalkDirection.right, TalkMsgType.message),
    
    TalkLog("박민호", "어디서 볼까요?",TalkDirection.left, TalkMsgType.message),
    TalkLog("노은지", "근처 카페에서 봐요",TalkDirection.right, TalkMsgType.message),
    
    TalkLog("박민호", "네, 있다가 뵐께요",TalkDirection.left, TalkMsgType.message),
    TalkLog("노은지", "네",TalkDirection.right, TalkMsgType.message),
]
 
예제코드는 상위와 같이 작성하시면 됩니다. 코드가 조금 맘에 안들죠? Left, Right를 구분해야하니깐요? 이부분은 추후에 데이터를 다루면서 변경할 예정입니다. ^^;;
 

 

 

 
채팅 UI 화면 만들기

 
body
var body: some View {
        VStack {
            ChattingList
            InputFieldView
        }
    }
body화면은 간단합니다. extension을 이용하여 추가된 View를 보여주기만 하면 끝입니다. 하지만 사용자 입력 부분을 VStack안에 넣어두면 문제가 되겠죠? 향후 ZStack을 이용하여 위치 조정을 해주셔야 합니다. 
 
 
extension of ContentsView
private extension ContentView {
    
    var ChattingList: some View {
        VStack {
            List {
                ForEach (TalkSamples) { (i) in
                     PersonMessage(i.Name, i.Message, i.Direction, i.type)
                }
            }
        }
    }
    
    var InputFieldView : some View {
        HStack {
            TextField("플레이스 홀더", text: .constant("텍스트필드"))
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .frame(width: 280, height: 80, alignment: .trailing)
            
            Spacer()
            Button(action: { print("Button 2") } ) {
                Text("Send")
            }
            .padding()
        }
    }
}
 
이어서 코드를 계속 살펴보면, 사용자의 대화가 저장된 TalkSamples 데이터를 List를 이용하여 가시화를 합니다. 중요한 부분은 PersonMessage를 참조하면 되겠네요?  사용자 입력은 형태만 추가할뿐이라서 상위와 같이 추가 후 넘어가도록 하겠습니다. 
 
 
먼저 구조체 PersonMessage를 보도록 하겠습니다. 
struct PersonMessage: View {
    
    var Name:String
    var ImgName:String
    var Message:String
    var Direction:TalkDirection
    var type:TalkMsgType
    let ImageWH:CGFloat = 50

    init( _ name:String, _ message:String, _ direct:TalkDirection, _ type:TalkMsgType) {
        self.Name = name
        self.Message = message
        self.Direction = direct
        self.type = type
        self.ImgName = "none"
        setImgName(self.Name)
    }
    
    mutating func setImgName(_ UserName:String ){
           
           for friend in FriendSamples {
               if friend.UserName == UserName {
                   self.ImgName = friend.ImgName
                   break
               }
           }
       }
    
    // 생략… 
}
 
코드가 길지는 않죠? 변수들과 초기화만 먼저 보도록 하겠습니다. 특이한 점들은 없지만, 좌측/우측을 구분하기위해 Direction을 사용하였습니다. 그리고 지금은 텍스트만 이용하여 출력을하고 있지만 message type을 설정 할 수 있도록 변수를 추가해두었습니다. 그리고 친구목록에 있는 이름을 확인 후 추가하는 작업 외에는 별 다른 작업은 없습니다. 
struct PersonMessage: View {
    // 생략… 

    var body: some View {
        HStack(alignment: .top) {
            if self.Direction == TalkDirection.left {
                LeftPerson
            }else {
               RightPerson
            }
        }
    }
}
 
body를 확인하면 HStack을 이용하여 구성합니다. 이때, 구조체 초기화를 할때, Left(상대방) 혹은 right(나) 인지에 따라 다른 인터페이스를 출력하도록 합니다.  코드를 더 간단하게 하기위해 좌우 위치만 바꾸도록 해도 되겠죠? 코드가 줄어들면 줄어들 수록 가독성이 늘어 난답니다. 
private extension PersonMessage {
    
    var LeftPerson: some View {
        HStack{
            VStack {
                Image(self.ImgName)
                    .resizable()
                    .frame(width: ImageWH, height: ImageWH)
                    .clipShape(Circle())
                Text(self.Name)
            }
            Text(self.Message)
            .frame(minWidth: 100, idealWidth: 300, maxWidth: 300,
                   minHeight: 10, idealHeight: 10, maxHeight: 100,
                   alignment: .topLeading)
            .background(RoundedRectangle(cornerRadius: 10).strokeBorder())
        }

    // RightPerson은 반대로 작성하시면 됩니다
        
    }
 
LeftPerson 코드만 가지고 왔습니다. L/R의 유무에 따라 HStack내부 위치를 변경하는 코드를 작성하셔도 되겠죠? 안되면 복사&붙여넣기하여 순서를 변경해주시면 됩니다. 
 
이제 다시 빌드 후 swiftUI 인터페이스를 빌드해보도록 합시다. 원하는대로 출력이 되나요? 기기에도 테스트를 꼭 해보세요
키보드 위치 변경하는 부분이 필요할테니깐요 🙂
 
포스팅 예고

 
 
다음 포스팅은 현재 만들어두었던 화면을 NavigationBar 및 TabView를 이용하여 연동하는 작업을 보도록 하겠습니다. 짧은 포스팅의 내용이였지만, 아무런 예제 없이 만들기는 힘들 수 있습니다. 그래도 직접 만들어 봄에 의미가 있으니, 블로그를 참조하셔서 직접 자신만의 예제를 만들어보세요~^^
 
그리고 상위 내용을 포스팅하고나면, 블로그에 계속 포스팅 해둔 node web server 연동 하는 작업과 iOS Device에 파일을 저장하는 모듈, 들을 하나씩 알아보도록 하겠습니다. :) 아직은 얼른 달려볼까요~?

이 글을 공유하기

댓글(0)

Designed by JB FACTORY