SwiftUI 채팅앱 만들기 마무리편

반응형
728x90
반응형
 
 
Introduction

  이번 포스팅은 앞서 만들었던 채팅로그인, 프로필, 채팅리스트, 친구목록, 채팅UI 화면들을 연동하는 작업을 수행하려고 합니다. 포스팅에 앞서 조금 걱정인 부분은 각 파트별로 혼선이 생기지 않도록 별도로 작업을 해두었는데, 결국 통합하는 과정을 한번 더 거쳐야 한다는 것이네요. 현재 블로그와 다르게 개발 진행이 많이 되어 새로 만들면서 포스팅하려는게 걱정이네요^^;
 
그래도 인터페이스부분만 다시하는거라 어렵지 않으니 새로 만들면서 블로그로 정리를 해둬야겠네요~^^
 
 
앞서 포스팅한 내용을 가지고, 하나의 프로젝트에 추가하여 계속 만들고 따라오시죠? 이번에는 신규 프로젝트를 생성하여 View전환관련된 내용만 정리하도록 하겠습니다. 설명에 있어 전체적인 코드를(?) 추가하지 않기 때문에 필요한 부분만 발췌하여 코드작성하시는게 더 도움이 될것 같아 이렇게 포스팅합니다. 그래도 설명은 쉽게 할 수 있도록! 노력하겠습니다. 
 
 
초기 셋팅

 
 
초기는 이전에 만들어 둔 View에 맞춰 이름을 작성하였습니다. 내용은 현재 보이는 코드와 같이 Text내용에 어떤 뷰인지 알 수 있도록 텍스트만 수정해두었습니다. 
 
 
화면전환

 
그림을 보면 아시겠지만, A화면에서 B화면으로 전환하는 것부터 알아보도록 하겠습니다. 처음에 App을 만들 때, 화면 전환은 어떻게 진행되는거지? 하고 고민했었는데 엄청 간단하게 해결이 되더군요. 그냥 다른 View를 생성자를 통해 호출하면 됩니다. 
 
먼저 A화면의 코드를 먼저 볼까요?
import SwiftUI

struct AppLoginUI: View {
    var body: some View {
        return NavigationView {
            ZStack {
                Color.yellow.edgesIgnoringSafeArea(.all) // 전체화면 색상 변경
                VStack{
                    LoginAction
                }
            }
            // navigation option
            .navigationBarHidden(true)
            .navigationBarBackButtonHidden(true)
        }
    }
}
 
Body부분부터 확인하도록 하겠습니다. 앞에서 계속 이야기한 NavigationView를 이용하고 있습니다. NavigationView를 이용하면, 다른 화면으로 손쉽게 전환이 가능합니다. 그리고 다음 화면의 상단에 각종 아이콘 등을 편하게 올려둘 수 있죠. 
private extension AppLoginUI {
    var LoginAction: some View {
        HStack{
            Spacer()
            NavigationLink( destination: MainTabView()
                                .navigationBarHidden(false)
                                .navigationBarBackButtonHidden(true) ){
                Text("App Main Login View")
            }
            Spacer()
        }.padding()
    }
}
 
그리고 이어서 LoginAction을 보도록 하겠습니다. NavigationView 안에서 다른 View로 전환하기위해서는 NavigationLink를 사용하시면 됩니다.  이때, destination에 전달하고 싶은 View를 작성하시면 됩니다. 그리고 여러 옵션으로 navigationBar를 숨기는 기능 등 설정이 가능합니다. 
 
참고로, Button을 이용해서 단순히 다른 View를 호출해서 넘어가도 됩니다. 
 
 
결과를 보고 넘어가야겠죠? 화면과 같이 넘어가시면 됩니다. 
 
 
App의 메인TabView 만들기

 
 
이어서 앞서 자주봤던 그림을 가지고왔습니다. 하단에 표기해둔 TabView가 보이시나요? 각각의 View를 선택하면 각기 다른 화면으로 전환됩니다. 
TabView코드를 먼저 확인하도록 합시다. 
import SwiftUI

struct MainTabView: View {
    
    private enum Tabs {
      case FriendList, ChattingRoom, Profile
    }
    
    @State private var selectedTab: Tabs = .FriendList
    
    var body: some View {
        ZStack {
            TabView(selection: $selectedTab) {
              Group {
                FriendListUI
                ChattingRoomUI
                ProfileUI
              }
            }
        }
    }
}
 
 
역시 간단한 코드!! 아닐까요?
 
먼저 TabView에서 사용되는 View에 맞춰서 열거체에 다음과 같이 코드를 작성합니다. 
    private enum Tabs {
      case FriendList, ChattingRoom, Profile
    }

    @State private var selectedTab: Tabs = .FriendList
 
selectedTab은 현재 선택되어진 Tab을 계속 값을 가지고 있기위해 사용합니다. 그럼 body에서 Tabview를 사용하는 방법을 알아 볼까요? 
var body: some View {
        ZStack {
            TabView(selection: $selectedTab) {
              Group {
                FriendListUI
                ChattingRoomUI
                ProfileUI
              }
            }
        }
    }
 
간단하죠?  유의하실점들도 몇가지 있지만, 코드를 변경하시고나서 항상 빌드 및 확인 후 진행하시기 바랍니다. 
 
그럼 이제 각각의 Tab을 선택하였을 때, 호출하는 방법을 알아보도록 할까요? 
private extension MainTabView {
    var FriendListUI: some View {
        FriendListView()
        .tag(Tabs.FriendList)
        .tabItem {Image(systemName: "person.3").imageScale(.large)}
        .navigationBarHidden(false)
        .onAppear {
            //self.TitleofNavi = AppMenu.FriendList.rawValue //"Friend List"
            //UITableView.appearance().separatorStyle = .none
        }
    }
}
 
먼저 첫번째 View로서 FriendListView()를 호출합니다. 그외에는 navigationBar의 옵션들이죠. 이때, 아래의 옵션은 꼭 추가해주셔야합니다.
.tag(Tabs.FriendList)
 
현재 선택된 Tab의 정보를 저장하는거니깐 꼭 필요하겠죠?
 
다음은 두번째 TabView를 보도록 하겠습니다. 
private extension MainTabView {
    var ChattingRoomUI: some View {
        ChattingRoomList()
        .tag(Tabs.ChattingRoom)
        .tabItem {Image(systemName: "message").imageScale(.large)}
        .navigationBarHidden(false)
        .onAppear {
            //self.TitleofNavi = AppMenu.ChattingRoom.rawValue //"Chatting Rooms"
        }
    }
}
호출하고자 하는 ChattingRoomList() View를 호출하고, Tag설정외에는 구성이 동일하죠? 
 
세번째 ProfileUI View를 호출하는 과정도 동일합니다. 
private extension MainTabView {
    var ProfileUI: some View {
        Profile()
        .tag(Tabs.Profile)
        .tabItem {Image(systemName: "number").imageScale(.large)}
        .navigationBarHidden(false)
        .onAppear {
            //self.TitleofNavi = AppMenu.Shop.rawValue //"Shap"
        }
    }
}
 
#결과
 
이렇게 화면 전화하는 건 이게 끝입니다. 
 
정말일까요? 네… 끝입니다. 단지, 여기서 추가 작업을 할 때 발생 한 오류들을 조금 정리하고 넘어가도록 하겠습니다. 본 포스팅에는 추가하지는 않았지만 각각의 View에 따라 제목(title)을 변경해야하는데 이를 위해, @State, @Binding 등의 사용방법을 알아둬야 합니다. 그리고 navigationBar를 이용하여 각 다른 View전환을 할때, 상단에 나타나는 Bar에 들어가는 시스템 아이콘을 추가할 때 문제가 발생하게 됩니다. 각각 다른 아이템을 넣고 싶은데 생각대로 쉽게 해결되지 않더라구요. 
 
오류메시지를 몇가지 정리하고 넘어가도록 하겠습니다. 이와 관련된 내용들은 조금 더 정리가 완료된 후, 문제점과 해결방법에 대한내용에 포커스를 맞춰서 포스팅 하도록 하겠습니다. 
 
 
 
Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types

 
#Reference
struct HeaderView: View {
        @State var choices = Choices.one
        var body: some View {
            switch choices {
            case .two:
                return AnyView(ChoiceTwoView(choices: $choices))
            default:
                return AnyView(ChoiceOneView(choices: $choices))
            }
        }
    }
 
SwiftUI NavigationBar & TabView 사용시 문제점

 
여기서 설명하기에는 내용이 너무 길어질 것 같아, 작업하시면서 꼭 확인하시기 바랍니다. 
 
 
향후포스팅

  다음 포스팅의 내용에 대해 리스트를 작성해두었습니다. 관련내용은 당연히 인터페이스 작업이 완료된 이후로서 다음과 같습니다. 
 
  • node.js web서버와의 연동
    • socket.io 기반 소켓 통신 (파일 전송 및 정보교환)
  • JSON을 이용하여 iOS device에 저장하
  • icloud를 이용하여 저장하기
  • Apple watch 연동
  • iOS 그래프 모듈 연동
  • 위젯 만들기
 
등등이 있습니다. :) 재미있는것들이 많죠? 저도 얼른 포스팅을 하고 싶어지네요. 블로그를 보면서 부족한 내용들이 있어 추가로 포스팅도해야하는데 어느것을 먼저할까 고민이 되네요. 밤새가면서 정리하는게 과연 나에게도 다른사람에게도 도움이 되는지는 잘 모르겠습니다. 만….
 
그래도 꾸준히 포스팅을하면서 필요한 자료들을 정리해나가야겠네요~^^
 
 
 
 
728x90
반응형

댓글

Designed by JB FACTORY