티스토리 뷰
새로운 프로젝트에 SwiftUI를 사용해보기 위해 블로그에 SwiftUI를 정리해보려 합니다. ㅎㅎ
조금 찾아보니까 애플이 정리해 놓은 공식 튜토리얼이 잘 되어 있더라구요? 혹시나 영문으로 공부하고 싶으신 분들은 해당 사이트 참고 바랍니다.
제가 앞으로 SwiftUI에 대해 정리하게 될 글 역시 모두 developer.apple.com/tutorials/swiftui를 기반으로 합니다!
SwiftUI란?
"SwiftUI is a modern way to declare user interfaces for any Apple platform. Create beautiful, dynamic apps faster than ever before."
> SwiftUI는 모든 Apple 플랫폼에 대한 사용자 인터페이스를 선언하는 현대적인 방법입니다. 그 어느 때보다 빠르게 아름답고 역동적인 앱을 만듭니다. 즉, 말 그대로 UI를 만드는 새로운 방법!
이곳을 방문하시면 애플이 친절하게 한글로 SwiftUI가 뭔지 잘 설명해 두었습니다.
장점:
1. 더 간단하고 깔끔한 코드 작성 가능
2. 실시간 미리 보기 제공(live preview)
단점:
1. iOS13, Xcode11 이후 버전만 지원
Tutorials1 - Creating and Combining Views
먼저 실습을 위해 프로젝트를 생성해야겠죠? 우리가 보통 프로젝트를 생성할 때랑 약간 차이가 있습니다. 프로젝트 생성 시 다음과 같이 인터페이스와 라이프사이클을 SwiftUI로 설정해야 합니다.
import SwiftUI
@main
struct LandmarksApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
프로젝트를 생성하면, LandmarksApp.swift 파일이 보이실텐데요. LandmarksApp구조체가 App 프로토콜을 채택하고 있는 것을 볼 수 있습니다. SwiftUI 앱 라이프 사이클을 사용하는 앱은 App 프로토콜을 준수해야 하고, body 프로퍼티는 하나 이상의 scene을 반환합니다. @main은 앱의 진입점을 가리키는 attribute identifier입니다.
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
그다음으로 ContentView.swift를 선택해 보시면, 위의 코드와 옆에 빈 여백의 창(Canvas)이 뜰 텐데요. 이 창을 통해 Preview를 볼 수 있습니다.
기본적으로 SwiftUI 뷰 파일은 두 가지 구조체를 선언합니다. 첫 번째 구조체는 View 프로토콜을 준수하며 뷰의 컨텐츠와 레이아웃을 묘사합니다. 두 번째 구조체는 해당 뷰에 대한 Preview를 선언합니다.
Canvas의 resume 버튼을 눌러보시면, "Hello, World!"를 확인해 보실 수 있습니다 ㅎㅎ
또 ContentView의 body 프로퍼티 내의 Text 안의 문장을 바꾸면 바로바로 Preview에 반영되는 것도 확인해 볼 수 있습니다. (신기)
이제 Command 버튼을 누른 상태에서 Preview의 TextView를 눌러보면 다음과 같은 화면을 볼 수 있는데요
여기서 SwiftUI Inspector를 통해 스토리보드의 Attribute Inspector와 마찬가지로 Text 내용을 바꾼다던지 폰트, 패딩 등등 여러 가지를 설정할 수 있습니다. preview에서 설정한 사항은 바로바로 코드에 반영되게 됩니다. (이것도 신기 신기)
SwiftUI View를 커스터마이즈 하기 위해서는 modifiers라는 메서드를 호출해야 합니다. 이 modifiers는 뷰를 랩핑하여 디스플레이나 다른 속성들을 변경합니다. 각각의 modifiers는 새로운 View를 반환하므로 여러 modifier를 수직으로 쌓듯이 연결하는 것이 일반적입니다.
다음과 같이 .font(.body), .fontWeight(.medium) 이런 것들이 다 modifier입니다.
struct ContentView: View {
var body: some View {
Text("Turtle Rock")
.font(.body) // modifier
.fontWeight(.medium) // modifier
.foregroundColor(.green) // modifier
}
}
SwiftUI Inspecto를 통해 변경하던지, 아니면 코드의 modifer를 수정 (여기서 수정이란 modifier를 제거 혹은 추가) 하던지 이러한 변경사항들을 Xcode는 즉각 업데이트합니다. (즉 바로바로 반영된다는 의미)
이번엔 스택을 사용해 볼 겁니다. 다들 스택 자주 사용하시죠??
Combine Views Using Stacks (일명 스택을 사용한 뷰 결합)
SwiftUI 뷰를 생성할 때, 뷰의 body 프로퍼티에는 content, layout 및 동작을 기술합니다. 하지만 이 body 프로퍼티는 오직 하나의 싱글 뷰만 반환합니다. ~~ 하지만 스택을 사용하면 이 뷰들을 결합시키고 임베드할 수 있습니다.
스택에는 총 3가지가 있습니다.
1. HStack 2. VStack 3. ZStack
Hstack은 Horizontal Stack 즉 수평 스택을 의미하고
Vstack은 Vertical Stack 즉 수직 스택을 의미합니다.
ZStack은 말 그대로 Z축을 기준으로 뷰를 쌓는 스택을 의미합니다. (ZStack안에 뷰를 여러개 넣는다고 가정하면 제일 상위뷰에 addsubview하는 느낌이라고 생각하면 됩니다.)
다음과 같이 코드상에서 Command 버튼을 누른 상태에서 Text를 클릭해보면 다음과 같이 popover가 뜰 텐데요. 뭐가 많죠..?
신기하게 프리뷰에서 SwiftUI Inspector 안 들어가고 코드상에서도 스택 사용이 가능하네요. 여기서 Embed in Vstack을 눌러줍시다.
그럼 Text가 Vstack으로 감싸 질 거예요. 그다음에 Xcode 우측 상단 + 버튼을 눌러 Text를 "Turtle Rock"텍스트 바로 아래 드래그합니다. (그럼 코드가 자동으로 생성될 거예요)
그다음 텍스트를 “Joshua Tree National Park” 로 변경하고 font도 subheadline변경, 그다음 스택의 정렬 기준을 leading으로 바꾸면
다음과 같이 됩니다!
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.green)
Text("Joshua Tree National Park")
.font(.subheadline)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
이번엔 Preview상에서 HStack을 사용해봅시다!
Preview에서 “Joshua Tree National Park”를 Command + 클릭을 하고 “Embed in HStack” 버튼을 눌러줍니다.
그다음에 다음과 같이, 공원의 주(state)를 나타낼 텍스트를 하나 더 추가!
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.green)
HStack {
Text("Joshua Tree National Park")
.font(.subheadline)
Text("California")
.font(.subheadline)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
이후 Hstack안의 두 Text 사이에 Spacer()를 넣어주게 되면 다음과 같이 두 텍스트 뷰가 서로 최대한 멀리 떨어지게 되는데요.
이 Spacer()의 역할은 다음과 같습니다.
상위 뷰의 모든 공간을 사용할 때까지 공간을 확장
위에 보시면 "Joshua~~"는 맨 왼쪽에 최대한 붙어있고 California 역시 우측으로 최대한 붙어있는 것을 볼 수 있죠?
이제 마지막으로 Vstack에 padding()을 주게 되면 다음과 같이 나름 보기 좋게 변하게 됩니다.
이번엔 정말 많이 사용되는 ImageView를 만들어 봅시다!
우선 이미지 뷰를 사용할꺼니까 이미지가 필요하겠죠? 애플이 튜토리얼에서 제공해주는 프로젝트를 다운로드하면 Assets에 공원 이미지가 들어가 있습니다. 해당 이미지를 Assets에 넣어주세요.
그다음에 SwiftUI View 파일을 하나 새로 생성해주세요. (Command + N)
파일명은 "CircleImage"
CircleImage 파일을 다음과 같이 수정하시면 프리뷰를 통해 정말 간편하게 원형 이미지가 들어간 이미지 뷰를 볼 수 있습니다.
import SwiftUI
struct CircleImage: View {
var body: some View {
Image("turtlerock") // 이미지
.clipShape(Circle()) // 원형으로 만들겠다
}
}
struct CircleImage_Previews: PreviewProvider {
static var previews: some View {
CircleImage()
}
}
여기에 overlay modifier와 shadow modifier를 추가하면 회색 원형 테두리와 그림자 추가까지 손쉽게 할 수 있습니다.
Use SwiftUI Views From Other Frameworks (다른 프레임워크에서 SwiftUI View 사용하기)
또 하나의 SwiftUI View 파일을 만들어 줍니다. 파일명은 "MapView" 그다음 MapKit을 import 해주세요.
import SwiftUI
import MapKit
struct MapView: View {
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 34.011_286, longitude: -116.116_868), span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2))
var body: some View {
Map(coordinateRegion: $region)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
}
}
그다음 위와 같이 지도에 대한 지역정보를 갖는 private state 변수를 하나 만들어주세요. private var는 다들 아시겠지만, @State는 처음 보시죠?
@State를 사용하면 하나 이상의 뷰에서 수정할 수 있는 데이터 소스를 설정할 수 있다네요. (뭔 소리야)
제 생각에는 아마 화면에 보여주는 데이터를 설정할 때 @State를 붙여주는 것 같습니다.
SwiftUI는 기본 스토리지를 관리하고 값에 따라 뷰를 자동으로 업데이트합니다.
var body: some View {
Map(coordinateRegion: $region)
}
State 변수 region 앞에 $가 붙어있는 것을 볼 수 있는데, 이처럼 State 변수 앞에 $를 붙이면 뷰에 바인딩할 수 있습니다.
이제 지도를 Preview에서 보려면 실시간 Preview로 전환해야 합니다.
우리가 지금 여태 3개의 뷰를 만들었죠?
아까 공원에 대한 정보를 담은 Text View 하나, 공원 Image View 하나, 그리고 마지막으로 Map View 하나
이제 이 세 개를 결합해서 최종 디자인을 만들겁니다. 다음과 같이요
ContentView.swift 파일로 들어가서 다음과 같이 VStack을 최상위에 하나 추가해주시고 그다음 스택 안에 우리가 아까 만든 Map View와 Image View를 추가한 후 modifier를 추가해 주시면 다음과 같이 최종적인 뷰를 볼 수 있습니다. 코드에 별도로 주석 달아놨습니다!
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
MapView()
.ignoresSafeArea(edges: .top) // 화면의 상단 가장자리까지 확대 (safeArea 무시)
.frame(height: 300) // width 생략시 뷰 width에 맞춰짐
CircleImage()
.offset(y: -130) // y축 기준 -130 offset
.padding(.bottom, -130) // 뷰 하단을 기준으로 -130만큼 패딩 지정
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
.fontWeight(.medium)
.foregroundColor(.green)
HStack {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
.font(.subheadline)
}
.font(.subheadline) // HStack 내부 전체에 해당 폰트 적용
.foregroundColor(.secondary) // HStack 내부 전체에 해당 컬러 적용
Divider() // 구분선 추가
// 부가 설명 추가
Text("About Turtle Rock")
.font(.title2)
Text("Descriptive text goes here.")
}
.padding()
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
뭔가 배우기 전에는 어렵게만 생각했는데, 뭔가 엄청 재밌네요 ㅋㅋ
읽어주셔서 감사합니다 :) 다음 튜토리얼로 돌아올게요~
'Swift&iOS > iOS' 카테고리의 다른 글
[iOS] 앱 디버깅 방법들 (0) | 2021.09.09 |
---|---|
[iOS] SwiftUI 기초 - 공식 튜토리얼 정리(2) (0) | 2021.04.15 |
[iOS] GCD - QoS, DispatchWorkItem, DispatchGroup 제대로 알고 쓰자! (2) (0) | 2021.04.13 |
[iOS] GCD - Dispatch Queue 제대로 알고 쓰자! (1) (1) | 2021.04.11 |
[iOS] Network - TLS 문제 해결 (0) | 2020.12.19 |