[iOS / SwiftUI Tutorials] VStack, HStack 을 사용해서 View 정렬하기

박준혁 - Niro·2023년 2월 15일
0

iOS App Dev Tutorials

목록 보기
2/2
post-thumbnail

안녕하세요 Niro 입니다!

iOS App dev tutorials 두번째 편으로 SwiftUI 의 선언 구문을 사용해서 View 를 만들고, 수정하고, 결합하여 App 의 UI 를 구성해보고자 합니다.

우리가 만들 화면은 View group 을 만들고 Stack 을 사용하여 정렬하고 타이머를 제작할 예정입니다.



📂 Project 만들기

1. Xcode 열기

App 만들기 위해서는 Project 를 생성해야겠죠?
Xcode 를 실행하게 되면 다음과 같은 화면을 볼 수 있습니다.

저희는 여기서 첫번째 메뉴인 Create a new Xcode project 를 눌러줍니다.

오른쪽에 있는 list 는 최근에 열었던 프로젝트를 볼 수 있고 제가 따로 흐릿하게 가려놨습니다....


2. iOS Project 설정하기

1번 과정을 수행하셨다면 다음과 같은 창이 보일겁니다.
여러개의 OS 를 선택할 수 있는 메뉴가 보이고 저희는 iPhone 용 앱을 만들기 때문에 iOS 에서 App 을 누르고 Next 로 이동해주세요.

프로젝트 옵션을 설정할 수 있는 창이 나오게 됩니다.
여기서 중요하게 볼 부분은 Product Nanme, Interface, Language 입니다.

일단 Interface 메뉴에서 SwiftUI 를 선택해주세요!

그 다음은 Lanaguage 에는 Swift 를 선택해주시면 됩니다.
Swift 가 나오기전 Object-C 라는 언어를 사용했는데 지금은 잘 사용하지 않습니다!

Product Name 은 Scrumdinger 로 설정해두었습니다.
프로젝트명이니 마음대로 작성해주셔도 괜찮습니다.

설정 후 Next 를 누르면 어디에 저장을 할지 창이 뜨는데 마음에 드는 곳에 저장해 두시면 됩니다!

그러면 이제 화면을 구성하러 가보실까요?

💁🏻 Xcode 를 처음 사용하는 경우 기본 화면 에 대해 알아보고 프로젝트를 만드는 방법을 알아보세요!



📐 View Group 만들기

View 는 App 의 구성요소로 UI 일부를 정의하게 됩니다.
작고 단순한 View 부터 복잡하고 거대한 View 까지 다양한 View 들이 여러 계층으로 구성되어 있습니다.

우리는 이번 Section 에서 타이머 화면의 Header 를 만들어 회의의 경과시간과 남은시간을 확인할 수 있도록 할 예정입니다.

프로젝트를 만들면 자동으로 생성된 여러 파일이 있는데 그 중 ContentView.swift 파일에 집중해주세요.

⌨️ ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

SwiftUI 파일의 가장 기초적인 구조는 두개의 Struct 로 구성되어 있습니다.

첫번째 Struct 인 ContentView 는 View Protocol 을 따르고 있으며 해당 Protocol 은 View 를 반환하는 body Property 가 있습니다.

body Property 에는 View 에 대한 내용, Layout 및 기능을 선언하게 됩니다.

두번째 Struct 인 ContentView_Previews 는 우측에 있는 가상 iPhone 에 표시할 해당 View 의 미리보기를 선언합니다.


가장 먼저 ContentView.swift 파일명을 앱의 용도에 맞게 MeetingView.swift 로 변경을 해줍니다.

이 작업은 단순히 하나의 파일명만을 바꾸는 것에 그치지 않습니다.

앞서 보셨던 두개의 Struct 이름을 한번에 바꿀 뿐만 아니라 ScrumdingerApp.swift 파일에 WindowGroup 에 있는 ContentView 이름 또한 바꿔야 하기 때문에 다음과 같이 메뉴를 통해 Rename 을 해주시면 한번에 바꿀 수 있다는 장점이 있습니다.

다만 하단에 있는 Preview Struct 는 이름을 직접 바꿔주세요!

자 파일명을 다 바꾸셨다면 이제 코드를 입력해볼까요?

💫 ProgressView 입력하기

⌨️ MeetingView.swift

Line 13 : body 안에 있는 내용을 다 지우시고 ProgressView 로 바꾸고 다음과 같이 데이터를 표기합니다.

Header 에 진행률과 경과된 시간을 표기해야 하기 때문에 백분율로 바꿔 표시하려고 합니다.

다만 앱이 데이터를 로딩하는 경우와 같이 확실하지 않은 진행률이 표시될 수 있다는 점 유의해주세요!

ProgressView 는 주로 SwiftUI 에서 가로로된 진행률을 나타내고자 사용하게 됩니다. 기본적으로 현재 진행률을 나타내는 value 는 Double 형으로 선언해주고 total 은 총합 ( 전체 진행률 ) 을 나타냅니다.

코드를 작성하고 나면 위와 같이 ProgressView 가 가운데에 출력된 것을 볼 수 있습니다.


🗄️ VStack, HStack 입력하기

⌨️ MeetingView.swift

이렇게 화면이 크게 있는데 다른 곳에 놓을 수는 없을까요?

Line 13 ~ 18 : SwiftUI 에서는 각 요소들을 가로, 세로 배치를 VStackHStack 을 통해 구현할 수 있습니다.
Stack 이라는 단어가 들어간 것 처럼 "각 요소를 세로 or 가로로 Stack 처럼 쌓는다" 라는 의미를 갖고 있다고 생각하시면 됩니다.

지금이야 코드가 짧기 때문에 VStack 을 직접 입력 후 안에 ProgressView 를 넣으면 되지만 안에 많은 요소들이 존재할 경우 다음처럼 진행하시면 됩니다.

ProgressView 에 커서를 가져다 놓고 command 를 누른후 클릭을 하게되면 드룹 메뉴가 나오는데 Embed in VStack 를 누르면 바로 VStack 안에 ProgressView 가 위치 하는 것을 볼 수 있습니다.

이렇게 V,H Stack 을 활용하여 View 의 요소들을 결합하고 계층화 시킬 수 있습니다.

V,H Stack 활용 방법에 대해 알아봤으니 각 요소들을 배치해볼까요?

⌨️ MeetingView.swift

다음과 같이 Text 와 Label View 를 배치 했습니다.

Line 15 ~ 24 : 각 Text 와 Label 은 VStack 안에 있으므로 세로 배치 되어 있지만 각 VStackHStack 에 있으므로 가로 배치가 된 것을 볼 수가 있습니다.

Label View 에서는 들어갈 text 와 이미지를 넣을 수가 있는데 systemImage 는 기호를 글꼴 처럼 처리하게 되어 설정에 따라 동적으로 크기가 조절 된다는 특징이 있습니다.

해당 기능을 사용하기 위해선 SFSymbol 안에 있는 기호들을 사용해주세요!



🛠️ View 수정 및 Style 지정하기

Header 안에 View 들을 만들었으므로 이제 스타일을 지정할 차례입니다. 제공하는 수정자를 추가 하여 모양을 조절하고 필요한 View 와 Stack 도 추가할 예정입니다.


📏 간격 넓히기

SwiftUI 는 기본적으로 가운데 정렬을 하게 되어있습니다. 그렇다면 각 요소에 간격을 주려면 어떻게 해야할까요?

⌨️ MeetingView.swift

Line 20 : Spacer() 를 활용하여 VStack 사이에 선언해 공백을 추가하므로써 간격을 넓힐 수가 있습니다.


📲 정렬 및 크기 수정 하기

⌨️ MeetingView.swift

간격을 넓혔다면 정렬을 해야겠죠?

Line 16, 21 : 원하는 VStack 이나 HStack 에 선행 (alignment: .leading), 후행 (alignment: .trailing) 정렬을 통해 각 View 들을 정렬할 수 있게 됩니다.

위의 큰 글씨는 큰 변화가 없어 보이지만 아래 있는 숫자들의 위치가 가장 맨 끝으로 정렬 된 것을 볼 수 가 있습니다.

⌨️ MeetingView.swift

Line 18, 24 : Text 의 크기를 줄이고자 .font(.caption) 수정자를 추가합니다.
Text 크기를 직접 입력하여 줄일 수 있지만 SwiftUI 에서 제공하는 수정자를 통해 더욱 깔끔한 App 을 만들 수 있습니다.


🧿 원형 타이머 및 바닥글 만들기

자 이제 화면 중앙에 위치한 원형 타이머 View 를 넣을 공간을 마련해 봅시다.

⌨️ MeetingView.swift

Line 26 ~ 27 : 진행률 바와 여러 텍스트 가 있는 Header 아래 위치 해야하므로 속해 있는 VStack 안에서 가장 아래 Circle() 을 선언 해줍니다.

Line 30 : 바닥글은 원 아래 위치해 있기 때문에 VStack 안에 선언해주고 Text 와 Button 이 같은 라인에 위치하므로 HStack 을 선언하여 안에 선언하고 떨어져 있으니 중간에 Spacer() 를 선언하여 공백을 만들어줍니다.

Button 은 아직 기능이 없으니 action 을 비워두도록 하겠습니다.

Line 36 : 기존에는 가장자리에 각 View 들이 붙어있어 보기가 안좋았습니다. 모든 View 를 감싸고 있는 최상단 VStack 에 .padding() 을 주어 가장자리로 부터 여백을 줍니다.



🔓 accessibility 데이터 보완

SwiftUI 는 Accessibility (접근성) 가 내장 되어 있어 추가할 작업 없이 지원을 받을 수가 있습니다. 예를 들어 Text View 로 이루어진 문자열 컨텐츠는 VoiceOver 를 통해 음성으로 읽을 수 있습니다.

그러나 경우에 따라 유추된 데이터를 보완하여 사용자의 접근성을 향상시킬 수도 있습니다.

⌨️ MeetingView.swift

import SwiftUI

struct MeetingView: View {
    var body: some View {
        VStack {
            ProgressView(value: 5, total: 15)
            HStack { ... }
                Spacer()
                VStack(alignment: .trailing) { ... }
            }
            .accessibilityElement(childeren: .ignore)
            .accessibilityLabel("Time remaining")
            .accessibilityValue("10 minutes")
            Circle()
                .strokeBorder(lineWidth: 24)
            HStack {
            	Text("Speaker 1 of 3")
                Spacer()
                Button(action: {}) {
                    Image(systemName: "forward.fill")
                }
                .accessibilityLabel("Next speaker")
            }
        }
        .padding()
    }
}

해당 요소가 Button 인지 image 인지 알려주는 것보다 각 요소가 어떤 의미를 갖고 있는지 목적을 알려주는 것이 accessibility 에 있어서 더욱 도움이 됩니다.

즉, 두 레이블의 대한 음성을 듣기 보다 중요한 정보를 알려주는 하나의 레이블로 데이터를 보완하는 것입니다.

  1. Header 에 있는 HStack 의 자식 View 에 대해 accessibility 레이블 및 값을 읽지 않도록 무시하게 합니다.
  2. 그다음 accessibilityLabel 에 의미 있는 이름을 저장합니다.
  3. 남은 시간에 대한 값을 accessibilityValue 에 저장합니다.

다음과 같이 작업을 하면 각 요소가 어떤건지 의미없게 알려주기 보다 그 요소가 어떤의미를 하고 있는지 직접 설정을 하여 의미있는 정보를 전달할 수 있게 됩니다.



✅ 마치며

이렇게 SwiftUI 의 구성 요소인 View 와 Stack, 수정자를 이용하여 View 를 꾸며보았습니다. 또한 accessibility 를 향상시키기 위해 라벨링을 따로 해주었습니다.

다음 편에는 각 회의를 한눈에 볼 수 있게 카드 형태로 꾸며볼 예정입니다.
잘못된 부분이 있다면 댓글로 알려주시고 좋은 글로 찾아올게요!

profile
📱iOS Developer, 🍎 Apple Developer Academy @ POSTECH 1st, 💻 DO SOPT 33th iOS Part

0개의 댓글