안녕하세요 Niro 입니다!
iOS App dev tutorials 두번째 편으로 SwiftUI 의 선언 구문을 사용해서 View 를 만들고, 수정하고, 결합하여 App 의 UI 를 구성해보고자 합니다.
우리가 만들 화면은 View group 을 만들고 Stack 을 사용하여 정렬하고 타이머를 제작할 예정입니다.
App 만들기 위해서는 Project 를 생성해야겠죠?
Xcode 를 실행하게 되면 다음과 같은 화면을 볼 수 있습니다.
저희는 여기서 첫번째 메뉴인 Create a new Xcode project 를 눌러줍니다.
오른쪽에 있는 list 는 최근에 열었던 프로젝트를 볼 수 있고 제가 따로 흐릿하게 가려놨습니다....
1번 과정을 수행하셨다면 다음과 같은 창이 보일겁니다.
여러개의 OS 를 선택할 수 있는 메뉴가 보이고 저희는 iPhone 용 앱을 만들기 때문에 iOS 에서 App 을 누르고 Next 로 이동해주세요.
프로젝트 옵션을 설정할 수 있는 창이 나오게 됩니다.
여기서 중요하게 볼 부분은 Product Nanme, Interface, Language 입니다.
일단 Interface 메뉴에서 SwiftUI 를 선택해주세요!
그 다음은 Lanaguage 에는 Swift 를 선택해주시면 됩니다.
Swift 가 나오기전 Object-C 라는 언어를 사용했는데 지금은 잘 사용하지 않습니다!
Product Name 은 Scrumdinger 로 설정해두었습니다.
프로젝트명이니 마음대로 작성해주셔도 괜찮습니다.
설정 후 Next 를 누르면 어디에 저장을 할지 창이 뜨는데 마음에 드는 곳에 저장해 두시면 됩니다!
그러면 이제 화면을 구성하러 가보실까요?
💁🏻 Xcode 를 처음 사용하는 경우 기본 화면 에 대해 알아보고 프로젝트를 만드는 방법을 알아보세요!
View 는 App 의 구성요소로 UI 일부를 정의하게 됩니다.
작고 단순한 View 부터 복잡하고 거대한 View 까지 다양한 View 들이 여러 계층으로 구성되어 있습니다.
우리는 이번 Section 에서 타이머 화면의 Header 를 만들어 회의의 경과시간과 남은시간을 확인할 수 있도록 할 예정입니다.
프로젝트를 만들면 자동으로 생성된 여러 파일이 있는데 그 중 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 는 이름을 직접 바꿔주세요!
자 파일명을 다 바꾸셨다면 이제 코드를 입력해볼까요?
Line 13 : body 안에 있는 내용을 다 지우시고 ProgressView
로 바꾸고 다음과 같이 데이터를 표기합니다.
Header 에 진행률과 경과된 시간을 표기해야 하기 때문에 백분율로 바꿔 표시하려고 합니다.
다만 앱이 데이터를 로딩하는 경우와 같이 확실하지 않은 진행률이 표시될 수 있다는 점 유의해주세요!
ProgressView
는 주로 SwiftUI 에서 가로로된 진행률을 나타내고자 사용하게 됩니다. 기본적으로 현재 진행률을 나타내는 value 는 Double 형으로 선언해주고 total 은 총합 ( 전체 진행률 ) 을 나타냅니다.
코드를 작성하고 나면 위와 같이 ProgressView
가 가운데에 출력된 것을 볼 수 있습니다.
이렇게 화면이 크게 있는데 다른 곳에 놓을 수는 없을까요?
Line 13 ~ 18 : SwiftUI 에서는 각 요소들을 가로, 세로 배치를 VStack
과 HStack
을 통해 구현할 수 있습니다.
Stack 이라는 단어가 들어간 것 처럼 "각 요소를 세로 or 가로로 Stack 처럼 쌓는다" 라는 의미를 갖고 있다고 생각하시면 됩니다.
지금이야 코드가 짧기 때문에 VStack
을 직접 입력 후 안에 ProgressView
를 넣으면 되지만 안에 많은 요소들이 존재할 경우 다음처럼 진행하시면 됩니다.
ProgressView
에 커서를 가져다 놓고 command 를 누른후 클릭을 하게되면 드룹 메뉴가 나오는데 Embed in VStack 를 누르면 바로 VStack
안에 ProgressView
가 위치 하는 것을 볼 수 있습니다.
이렇게 V,H Stack 을 활용하여 View 의 요소들을 결합하고 계층화 시킬 수 있습니다.
V,H Stack 활용 방법에 대해 알아봤으니 각 요소들을 배치해볼까요?
다음과 같이 Text 와 Label View 를 배치 했습니다.
Line 15 ~ 24 : 각 Text 와 Label 은 VStack
안에 있으므로 세로 배치 되어 있지만 각 VStack
은 HStack
에 있으므로 가로 배치가 된 것을 볼 수가 있습니다.
Label View 에서는 들어갈 text 와 이미지를 넣을 수가 있는데 systemImage
는 기호를 글꼴 처럼 처리하게 되어 설정에 따라 동적으로 크기가 조절 된다는 특징이 있습니다.
해당 기능을 사용하기 위해선 SFSymbol 안에 있는 기호들을 사용해주세요!
Header 안에 View 들을 만들었으므로 이제 스타일을 지정할 차례입니다. 제공하는 수정자를 추가 하여 모양을 조절하고 필요한 View 와 Stack 도 추가할 예정입니다.
SwiftUI 는 기본적으로 가운데 정렬을 하게 되어있습니다. 그렇다면 각 요소에 간격을 주려면 어떻게 해야할까요?
Line 20 : Spacer() 를 활용하여 VStack 사이에 선언해 공백을 추가하므로써 간격을 넓힐 수가 있습니다.
간격을 넓혔다면 정렬을 해야겠죠?
Line 16, 21 : 원하는 VStack 이나 HStack 에 선행 (alignment: .leading)
, 후행 (alignment: .trailing)
정렬을 통해 각 View 들을 정렬할 수 있게 됩니다.
위의 큰 글씨는 큰 변화가 없어 보이지만 아래 있는 숫자들의 위치가 가장 맨 끝으로 정렬 된 것을 볼 수 가 있습니다.
Line 18, 24 : Text 의 크기를 줄이고자 .font(.caption)
수정자를 추가합니다.
Text 크기를 직접 입력하여 줄일 수 있지만 SwiftUI 에서 제공하는 수정자를 통해 더욱 깔끔한 App 을 만들 수 있습니다.
자 이제 화면 중앙에 위치한 원형 타이머 View 를 넣을 공간을 마련해 봅시다.
Line 26 ~ 27 : 진행률 바와 여러 텍스트 가 있는 Header 아래 위치 해야하므로 속해 있는 VStack
안에서 가장 아래 Circle()
을 선언 해줍니다.
Line 30 : 바닥글은 원 아래 위치해 있기 때문에 VStack
안에 선언해주고 Text 와 Button 이 같은 라인에 위치하므로 HStack
을 선언하여 안에 선언하고 떨어져 있으니 중간에 Spacer()
를 선언하여 공백을 만들어줍니다.
Button 은 아직 기능이 없으니 action 을 비워두도록 하겠습니다.
Line 36 : 기존에는 가장자리에 각 View 들이 붙어있어 보기가 안좋았습니다. 모든 View 를 감싸고 있는 최상단 VStack 에 .padding()
을 주어 가장자리로 부터 여백을 줍니다.
SwiftUI 는 Accessibility (접근성) 가 내장 되어 있어 추가할 작업 없이 지원을 받을 수가 있습니다. 예를 들어 Text View 로 이루어진 문자열 컨텐츠는 VoiceOver 를 통해 음성으로 읽을 수 있습니다.
그러나 경우에 따라 유추된 데이터를 보완하여 사용자의 접근성을 향상시킬 수도 있습니다.
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 에 있어서 더욱 도움이 됩니다.
즉, 두 레이블의 대한 음성을 듣기 보다 중요한 정보를 알려주는 하나의 레이블로 데이터를 보완하는 것입니다.
다음과 같이 작업을 하면 각 요소가 어떤건지 의미없게 알려주기 보다 그 요소가 어떤의미를 하고 있는지 직접 설정을 하여 의미있는 정보를 전달할 수 있게 됩니다.
이렇게 SwiftUI 의 구성 요소인 View 와 Stack, 수정자를 이용하여 View 를 꾸며보았습니다. 또한 accessibility 를 향상시키기 위해 라벨링을 따로 해주었습니다.
다음 편에는 각 회의를 한눈에 볼 수 있게 카드 형태로 꾸며볼 예정입니다.
잘못된 부분이 있다면 댓글로 알려주시고 좋은 글로 찾아올게요!