SwiftUI로 채팅 로그인화면 만들기

 
 
Introduction

  SwiftUI를 시작하기위해서는 이전 포스팅(Xcode에서 SwiftUI시작하기)을 확인 해주시기 바랍니다. 이후의 포스팅에서는 SwiftUI코드와 결과화면에 집중하여 포스팅을 진행합니다. 블로그의 포스팅을 보다가 궁금하신 부분들은 댓글로 알려주시면 시간이 날 때, 포스팅에 추가 업데이트 하도록 하겠습니다. 
 
 
SwiftUI 채팅 로그인 화면만들기 프롤로그

 
#결과화면 만들어보기 
 
 
 
Balsamiq Mockups를 이용하여 채팅화면UI를 위와 같이 구성을 해보았습니다.  대부분의 채팅App화면과 유사하게 구성을 하였습니다. 참고로, 기능 구현은 최종 채팅 App 화면UI를 완료 후 개발진행하려고 합니다. 그리고 개발이 완료 된 이후에 포스팅을 이어서 진행예정으로 시간이 다소소요됨을 미리 이야기 드립니다. 
 
  • 참고: Balsamiq Mockups는 위 연결된 링크를 따라 들어가서 다운받아 Win/Mac에서 사용할 수 있습니다.

 

 

 
#무엇이 필요할까?
  개발에 앞서, 화면UI와 관련하여 무엇이 필요할까? 고민을 해보신적 계신가요? 저도 책을 처음부터 끝까지 반복을 통해 지식을 습득하는 편이라, 이번에 책을 사서 4~5번 정도 정독하였습니다. 하지만, 정작 만들고자 하는 부분에서는 책의 지식을 응용해서 만들어야 하는데 바로 떠오르지 않죠? 그럴 때 저는 상위 그림과 같이 화면UI를 만들어두고, 무엇이 필요한지 checkList를 작성합니다. 그리고 원하는 UI가 만들어질 때까지 소스코드를 조금씩 조금씩 고쳐서 만들죠. 물론 똑똑하신 분들은 한번에 만들기도 하지만, 처음 접하시는 분들에게는 어떤 것들이 필요할지 고민 후, 책의 목차를 통해 찾아서 작업을 하는 방법을 추천드립니다. 
 
 
CheckList
  • TextField
    • 사용자의 ID 및 E-mail 주소를 입력받기 위해 필요 
  • SecureField
    • 비밀번호 입력을 위해 필요 
  • Button
    • 로그인 버튼 
    • 계정찾기, 비밀번호 찾기 
  • Toggle
    • 자동로그인 기능 구현
  • 기타
    • VStack, HStack
      • App화면 구성을 위해서 사용
    • Text
      • Label을 이용하여 App의 이름을 표기 
 
결과 및 레이아웃 미리 보기 

 
 
결과 화면은 위와 같습니다. 기본적으로 로그인을 위해 다음과 같이 구성하였습니다. 결과 모습을 보여드리는 것은 먼저 만들고자 상상하는 화면을  Balsamiq Mockups으로 구성할 때, 무엇이 필요한지, 어떻게 구성을 할 것인지 꼭 필요 합니다. 
 
 
SwiftUI 채팅 로그인 화면만들기 - 배경색상

 
App에 색상부터 먼저 지정해보도록 하겠습니다. 
 
ContentView.swift 
import SwiftUI
struct ContentView: View {
   var body: some View {
       // 전체화면 색상 변경
       Spacer().background(Color.blue)
   }
}
struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
       ContentView()
   }
}
 
결과 화면은 다음과 같습니다. 
 
 
 
 
SwiftUI 채팅 로그인 화면만들기 - 배경색상2

  시간이 흘러 포스팅을 이어서 하게 되었습니다. 앞서 설명한 전체 배경색상 부분에서는 노치부분과 Bottom 부분에서는 색상 표기가 되지 않는 문제가 있습니다. 이러한 문제를 해결하기위해서는 다음의 소스코드와 같이 간단히 해결 가능합니다. 
import SwiftUI

struct ContentView: View {
    var body: some View {
        ZStack {

            // method 1
            Spacer().background(Color.blue).edgesIgnoringSafeArea(.all)
            
            // method 2
            //Color.blue.edgesIgnoringSafeArea(.all)
            
            Text("Hello, World!")
        }
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
 
아래의 코드와 같이 2가지 방법으로 사용 가능합니다. 물론 Zstack {} 내부에 있을 때만 입니다. 다른 VStack, HStack을 사용할 경우 적절한 위치에 작성하시기 바랍니다. 
            // method 1
            Spacer().background(Color.blue).edgesIgnoringSafeArea(.all)
            
            // method 2
            Color.blue.edgesIgnoringSafeArea(.all)
 
 
1)구조체2)extension을 이용한 contentview 내용 추가 

 
결과는 왼쪽 그림과 같이 App Name 인 Title을 출력하고자 하는 것입니다. 간단하죠? 타이틀을 출력하는 방법은 구조체를 이용하여 호출 하는 방법contentsView의 extension을 이용하여 생성된 변수로 출력하는 방법 2가지가 있습니다. 
import SwiftUI

struct ContentView: View {
    var body: some View {
        ZStack {
            Color.blue.edgesIgnoringSafeArea(.all)
            VStack{
                AppTitle1   // method 1
                AppTitle2() // method 2
            }
        }
    }
}
 
상위 코드에서 method 1,2의 차이가 보이죠? 구조체를 호출하느냐, 혹은 변수를 호출하느냐의 차이입니다. 그럼 이제 구현된 코드를 살펴보도록 하겠습니다. 
 
method 1: extension을 이용한 가시화 방법
private extension ContentView {
    var AppTitle1: some View {
        VStack {
            Text("App Name")
                .font(.title)
                .fontWeight(.medium)
                .padding()
        }
        .frame(maxHeight: 150,alignment: .center )
    }
    
}
method 2: 구조체를 이용한 가시화 방법
struct AppTitle2: View {
    var body: some View {
        VStack {
            Text("App Name")
                .font(.title)
                .fontWeight(.medium)
                .padding()
        }
        .frame(maxHeight: 250,alignment: .center )
    }
}
이제 코드를 확인해보면 차이점이 확 들어나죠? 장단점이 무엇일까요? 제가 공부했던 책을 참조하여 extension으로 계속 작성을 했었는데, 나름 코드가 깔끔하게 정리하기 편하고 괜찮았습니다. 하지만, 구조체를 써야하는 경우가 있었습니다. 왜일까요?  구조체의 생성자를 이용하여 초기화 값을 전달하기 위해서입니다. 그 이유는 이후 포스팅에서 코드로 볼 수 있겠지만, 반복되어 출력이 되는 UI가 있을 경우, 동일 한 코드를 두세번, 혹은 여러번 사용하기위해서 extension을 이용하는 것보다 구조체를 이용하는 것이 장점입니다. 
 
이후 코드를 참조하여 만드시는 분들의 판단에 따라 코드를 작성하시면 됩니다. 항상 정답은 없으니까, 블로그 포스팅은 조금씩 섞어서 사용하니 꼭 두가지 방법 검토하여 사용하시기 바랍니다. 

 

 

 
로그인 정보 (E-mail Address, Password) 추가하기 

 
 
이번에는 상위 그림과 같이 로그인을 하기 위한 공간을 만들어보도록 하겠습니다. 먼저 입력 공간 좌측편에 아이콘 두개가 있습니다. 이 아이콘은 Apple에서 기본적으로 제공하는 아이콘으로서 적절한 이름만을 가지고 사용할 수 있습니다. 하지만, 이 많은 아이콘들의 이름을 기억할 수 없기 때문에 다음의 App을 이용하시면 됩니다. 
 
 
SF simpbols app for macOS를 블로그에 따로 포스팅 해두었습니다. 상위 프로그램에서 필요한 아이콘의 이름을 찾아 사용하시면 됩니다. 
HStack{
  Image(systemName: "envelope").frame(width: iconWidth)
  TextField("ID / E-mail Address", text: $email)
    .frame(width: inputWidth, height: inputHeight)
    .padding()
    .background(RoundedRectangle(cornerRadius: 10).strokeBorder())
}
 
상위 코드에서는 시스템편지 아이콘(evelope)를 이용하여 추가하였습니다. 그리고 ID or E-mail Address로 TextField()를 이용하여 작성하였습니다.  그외에는 frame의 width, height 를 설정하였습니다. 마지막으로 입력 박스의 라운딩을 통해 부드럽게 보이도록 표현하였습니다. 
HStack{
    Image(systemName: "lock").frame(width: iconWidth)
    SecureField("Password", text: $password)
      .frame(width: inputWidth, height: inputHeight)
      .padding()
      .background(RoundedRectangle(cornerRadius: 10).strokeBorder())
}
 
비밀번호를 입력하는 곳은 SecureField()를 이용하여 앞서 작성한 textField()와 동일한 형태로 작성하였습니다. 두개의 UI차이점은 다음과 같습니다. 
 
 
차이점이 보이시나요? 입력을 하였을 때 보이는지, 혹은 가려지는지
 
 
로그인 버튼 만들기

 
이번에는 자동로그인로그인 기능을 수행하는 버튼 작성방법에 대해 알아보도록 하겠습니다. 
struct LoginAction: View {
    @State private var isOn = true
    
    init() {
        UISwitch.appearance().onTintColor      = .gray
        UISwitch.appearance().thumbTintColor    = .white
    }
    
    var body: some View {
        HStack{
            Toggle(isOn: $isOn) {
                Text("자동")
            }
            .frame(width: 90, height: 40)
            
            Button(action: { } ) {
                Text("로그인")
                    .frame(width: 80, height: 10)
                    .padding()
                    .background(RoundedRectangle(cornerRadius: 10).strokeBorder())
            }
        }.padding()
    }
}
 
전체 코드를 먼저 보도록 하겠습니다. 역시 짧고 쉽죠? 
 
자동로그인 버튼을 위해서 Toggle()을 이용을 하면 됩니다. 이때, 자신만의 App에 색상을 넣고 싶을텐데, 다음과 같이 초기화가 될 때 apperance()를 이용하여 값을 설정해주시면 됩니다. 
    init() {
        UISwitch.appearance().onTintColor      = .gray
        UISwitch.appearance().thumbTintColor    = .white
    }
그외에는 특별한 부분이 들어가지 않은 코드라서 설명을 마치도록 하겠습니다. 
 

 

 

 
Bottom Menu 만들기

 
마지막으로 Bottom Menu로 계정찾기, 비밀번호찾기 기능이 있습니다. 기능구현을 위해 간단히 버튼을 이용하여 가시화를 하도록 하겠습니다. 
struct LoginAccountManage: View {
    var body: some View {
        HStack (spacing:20) {
            Button(action: {}) {
                Text("계정찾기")
            }
            Button(action: {}) {
                Text("비밀번호찾기")
            }
        }
        .frame(maxHeight: 250, alignment: .bottom )
        .padding()
    }
}
 
갑자기 마무리?

  참 쉽죠….?? 아직 기능이 들어가지 않은 순수 UI에 대해서만 설명하였기때문입니다.  코드를 복사&붙여넣기하여 이후는 frame의 속성값들을 변경하면 간단하게 해결된 일들입니다. 이렇게, 저는 개발을 할 때 UI만 빠르게 개발하고 이후에 만들어진 알고리즘을 연결하는 형태로 진행하고 있습니다. 혼자 작업을 할 때, UI 및 기능들을 바로 구현해서 만들기도하지만 향후 다른 인원과의 co-work을 위해서는 작업을 분리해서 진행하는 방법도 필요하지 않을까 고민하여 이런저런 방법들로 시도하고 있습니다. 
 
성공하는 한가지 방법보다 역시, 실패하는 10가지를 해보는게 조금 더 의미 있는 작업이지 않을까라는 생각을 하며… 
 
다음 블로그 포스팅은 "프로필 화면 만들기"를 해보도록 하겠습니다. 
 
 
 
 
 
 
 
 

이 글을 공유하기

댓글(0)

Designed by JB FACTORY