iyOmSd/Title: Swift

[Swift] Socket.IO를 이용한 소켓통신 채팅앱 만들어보기 (2/3)

냄수 2019. 12. 17. 16:54
반응형

이전 글에서는 소켓을 이용해서 간단하겍 서버와 데이터를 주고 받는 걸 해봤어요

이제 그 데이터를 이용해서 채팅방처럼 구현해보도록 해볼게요!!

 

 

<서버는 Echo역할을 해주도록 설계해놨습니다.!!>

 

채팅구조체 생성

우선 간단한 구조체를 하나 만들었어요

채팅방을 보면 내가친 대화랑 남들이친 대화가 구분되잖아요?

type은 그 대화를 구분짓는 변수구요

message는 말그대로 메시지를 담을 변수가 됩니다

 

 

서버소켓 데이터 수신

 

코드 박스로 쓰려고했는데 가독성이 사진이 더 좋을것 같아서(?) 사진으로 가져왔어요!!

위의 코드는 서버로부터 온 소켓 데이터를 받아서 처리하는 함수입니다!

 

한번 살펴보면

  • 'test'라는 이벤트이름으로 날라온 데이터를 수신

  • 채팅 구조체생성

  • dataArray의 타입을 확인

  • Dictionary형태로 형변환후

  • Key-Value형태이므로 알맞은 Key값으로 데이터를 꺼내와서 구조체에 저장

  • myChat이라는 배열에 구조체 저장

  • updateChat(밑에 코드설명) 채팅업데이트 실행

과정을 거쳤네요!! 물론 다른 방법으로도 해도 되요!!  

제 코드는 참고용 으로만 봐주세요!!

 

위에서 실행한 updateChat함수를 봐볼게요이제

 

 

채팅 수신 업데이트

이과정은 TableView에 데이터가 수신될 때 새로운 행을 추가해서

채팅하는 느낌을 주도록(?)하는 부분이에요

 

beginUpdates - 업데이트를 할것이다

insertRow - 해당행에 cell추가

endUpdates - 업데이트 끝

 

이 3개의 함수로 채팅말풍선을 추가할 수 있습니다!!

 

행을 추가할때 제일 아래에 추가해야되서

구조체배열에 있는 마지막행에 추가를 하면되므로

myChat.count - 1를 해줬구요

애니메이션은 원하는걸로 지정해주시구요

저 같은경우는 껏어요 여러개의 내용이 올때 약간 보기안좋았어요

 

그리고 채팅이 오면 자동으로 밑으로 내려가도록 설정도 해봤어요

이 기능은 조건에따라 바꿔서 써야 할것 같아요

위로 스크롤해서 이전의 내용을 보다가 채팅이와서 내려가버리면 불편하잖아요?

 

scrollToRow를 이용해서 마지막행으로 스크롤 되도록 해줬어요

 

 

채팅 송신 업데이트

채팅은 받기만 하는게아니라 

보내기도하잖아요?!

 

TextField의 문자열을 받아서

서버에 'test' 라는 이벤트이름으로 emit 해줍니다

 

채팅을 저장하는 구조체배열에 추가도 해주구요

 

마찬가지로 updateChat함수를 실행시켜서 채팅목록을 업데이트 해줍니다!!

 

 

이렇게만 되면 데이터를 주고받는데에는 지장이 없어요

짧은 메시지를 주고받을때는 상관없지만

긴 내용의 메시지가 오간다...?

그러면 테이블 Cell의 크기가 메시지에 맞게 동적으로 움직여야

짤리지않고 모든내용이 출력가능해요

 

 self.tableView.rowHeight = UITableView.automaticDimension

이 한줄이면 ContentSize에 맞게 자동으로 행높이를 조절해줘요

‼️주의사항이 있어요‼️

오토레이아웃이 잘 잡혀있어야 ContentSize를 알수있어서

동적으로 움직여요!!

 

 

말풍선

채팅창을 만드는데 텍스트만 있으면 심심하잖아요

그래서 위처럼 말풍선을 만들어 줄거에요!!

 

먼저 말씀드릴게 있어요!!

저는 먼저 코드로 구현했는데

스토리보드로 구현하는게 더 간단하고 이쁘게 잘나오더라구요!!

 

코드로 구현하는법도 알아두면 좋을거같아요...ㅠㅠ

삽질을 너무했었어요..

 

스토리보드로 구현하는법은 밑에 설명이 있습니다!!

 

let size = CGSize(width: chatLabel.frame.width, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
            
estimatedFrame = NSString(string: msg).boundingRect(with: size, options: options, attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 17)], context: nil)
            
bubbleView.frame = CGRect(x: self.view.frame.width - estimatedFrame.width - 40 - 5, y:chatLabel.frame.origin.y - 5 , width: estimatedFrame.width + 20, height: estimatedFrame.height + 10)
bubbleView.backgroundColor = UIColor(red: 255/255, green: 249/255, blue: 184/255, alpha: 1.0)

 

 

size

말풍선의 최대 사이즈를 설정해요

 

options

NSStringDrawingOption.....어쩌구저쩌구 위처럼 설정해줘요

느낌상 글씨의 처음부터 끝까지의 origin좌표를 주는옵션 느낌이에요

 

estimatedFrame

위에서 설정한 size와 options을 이용하고 msg문자열을 폰트와 크기를 설정해서

CGRect형 값을 받아요

해당 문자열의 크기를 받는다고 생각하면되요

 

방금 위의 estimatedFrame 값으로 말풍선이될 view의 frame에 적용해주면되요

저같은경우는 화면의 가로길이에서 문자열의 길이만큼 뺀위치에서 마진 -40을 주도록 설정했어요

저코드는 제가 친 채팅이 오른쪽에 보이도록하기위해서 저런 식을 사용했어요

 

 

이렇게만하면 스크롤시 뷰의 크기가 마구 변하더라구요

재사용되기때문에 값을 정확히 알지못해서 그런거같아요!!

 

그래서 셀안에 view와 estimatedFrame값을 넣어두고 사용했어요

 

그리고 

또한 중요한 함수에요

뷰를 생성했기때문에 뷰의 구조상 제일위에 뜨는데

해당 뷰는 채팅을 가리게되죠

채팅이 보이지않기때문에

Label 뒤로가도록 설정해주는 코드에요 

 

키보드 대응

self.chattingViewBottomConstraint.constant = height
self.tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: height, right: 0)
self.tableView.scrollToRow(at: IndexPath(item: chats.count - 1, section: 0), at: .bottom, animated: false)

키보드를 올리면 채팅도 따라서 올라가도록 설정하는 코드에요

채팅을 치는 뷰의 키보드의 높이만큼 레이아웃을 위로 조절하고

테이블뷰의 inset을 다시설정해주고

<< 그전에 스토리보드에서 테이블뷰의 오토레이아웃을 슈퍼뷰에 걸어야해요!! >>

채팅 위치도 아래가 그대로 보이도록 스크롤해주었어요

 

 

이렇게만 하면 어느정도 있어보이는 채팅앱을 만들 수 있어요!!

하지만 약간 위치 맞추기가 어렵더라구요 그래서 스토리보드로!!!

하는걸 추천드려요

 

스토리보드 구현

 

셀도 다시 새롭게 수정했어요

간단해졌죠??! 라벨만잇어요

 

테이블뷰 디자인은 이렇게

아예 라벨안에 뷰를 만들고 오토레이아웃을 걸어줘요

 

Greater than or Equal를 사용해서

왼쪽 여백이 100 이거나 100보다 크게 늘어나도록 설정해줬어요

텍스트길이에 따라서 유동적으로 가로길이가 늘어나는 설정이에요!!!

 

그리고 이제 추가해줘야할 코드가 있어요!!

 

전에는 뷰를 코드로 생성해서 estimated를 설정해서 줬지만

셀에 말풍선이 될 뷰를 만들었기때문에

셀높이를 조절해서 말풍선의 크기를 조절해야해요

 

TableViewDelegate를 이용해서 셀의 높이를 계산해서 줄거에요

위에서 썻던 코드와 같아요!!

여기서 주의해야 할것은 해당 라벨의 오토레이아웃의 마진을 잘 확인해야해요

그래야 이쁘게 딱딱 맞춰서 나와요

 

위에서 코드로한것과 비교했을때 확실히 간편하고 이쁘게 잘나와요

제가 물론 코드로 잘못 짯을수도 있어요

그렇듯 코드로 하는게 더어렵다는거겟죠???

 

키보드 처리까지하고보니 일반 채팅창같이 이쁘게 잘나왔네요!!!

 

 

 

진짜 5일동안 내내했는데

헤매고 삽질하느라 시간을 많이쓴거같아요

 하고보니 너무 한거없어보이는데

소켓을 만들면서 많은 생각과 고민을 하게 됬고

TableView를 만들때도 많은 생각을 하게 해준 연습 프로젝트였어요

 

 

 

아래는 전체코드에요

class SecondViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var botViewLayout: NSLayoutConstraint!
    @IBOutlet weak var textField: UITextField!
    var myChat: [chatType] = []
    var socket: SocketIOClient!
    //키보드 높이를 저장할 변수
    var keyboardHeight: CGFloat!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.socket = SocketIOManager.shared.socket
        bindMsg()
        
        self.tableView.delegate = self
        self.tableView.dataSource = self
        
        initGestureRecognizer()
        registerForKeyboardNotifications()
    }
    
    // 뒤로가기시 소켓종료
    override func viewWillDisappear(_ animated: Bool) {
        SocketIOManager.shared.closeConnection()
        unregisterForKeyboardNotifications()
    }
    
    // 서버로부터 메시지 받을때 채팅창 업데이트
    func bindMsg() {
        self.socket.on("test") { (dataArray, socketAck) in
            var chat = chatType()
            print("***************************************")
            print(type(of: dataArray))
            let data = dataArray[0] as! NSDictionary
            
            chat.type = data["type"] as! Int
            chat.message = data["message"] as! String
            self.myChat.append(chat)
            print(chat)
            
            self.updateChat(count: self.myChat.count) {
                print("Get Message")
            }
        }
    }
    
    // 채팅 업데이트
    func updateChat( count: Int, completion: @escaping ()->Void ) {
        
        let indexPath = IndexPath( row: count-1, section: 0 )
        
        self.tableView.beginUpdates()
        self.tableView.insertRows(at: [indexPath], with: .none)
        self.tableView.endUpdates()
      
        self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: false)
		
        // 필요한경우 escaping closure를 이용한 데이터 전달
		completion()
    }
    
    // 전송버튼
    @IBAction func sendMsgButtonClick(_ sender: Any) {
        let text = self.textField.text!
        self.socket.emit("test", text)
        
        self.myChat.append(chatType(type: 0, message: text))
        
        self.updateChat(count: self.myChat.count) {
            print("Send Message")
        }
    }
}

struct chatType {
    var type = -1
    var message = ""
}

extension SecondViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let chat = myChat[indexPath.row]

        //좌우마진 122, 40이 최대값이므로 최댓값 가로길이는 아래와같음
        let widthOfText = view.frame.width - 122 - 40
        let size = CGSize(width: widthOfText, height: 1000)
        let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17)]
        let estimatedFrame = NSString(string: chat.message).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)

        // 위아래마진 14,14 + 여유공간 4
        return estimatedFrame.height + 14 + 14 + 4
    }
}
 

extension SecondViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.myChat.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cellId = self.myChat[indexPath.row].type == 0 ? "MyCell" : "YourCell"
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! ChatTVC
        cell.chatLabel.text = self.myChat[indexPath.row].message
        return cell
        
    }
}

 

 

 

⬇️⬇️소켓 구현⬇️⬇️

2019/12/13 - [iyOmSd/Title: Swift] - [Swift] Socket.IO를 이용한 소켓통신 채팅앱 만들어보기 (1/3)

 

[Swift] Socket.IO를 이용한 소켓통신 채팅앱 만들어보기 (1/3)

안녕하세요😄😄 소켓을 공부하려고 마음 먹은지 꽤 오래전이지만 드디어 하게되네요 소켓으로 할 수 있는 대표적인걸로는 채팅이 있죠 그래서‼️ 채팅앱을 만들어 보려고해요 보기엔 쉬워보이지만 생각보다 많이..

nsios.tistory.com

⬇️⬇️서버 구현⬇️⬇️

2019/12/17 - [iyOmSd/Title: Swift] - [Swift] Socket.IO를 이용한 소켓통신 채팅앱 만들어보기 (3/3)

 

[Swift] Socket.IO를 이용한 소켓통신 채팅앱 만들어보기 (3/3)

이번게시물은 Swift 관련 게시물이아니지만... 전 게시물과 이어지는 관계로 그냥 썼어요,,, 앞에서 소개한 소켓을 테스트해보려면 서버가 필요하잖아요!?!?! 제가 서버를 잘하진 못해서 간단하게 구현한 서버를..

nsios.tistory.com

 

반응형