안녕하세용가리🦕
오늘은 SwiftUI 공식 문서 Tutorials을 따라 해보면서 공부해보려고 합니당
SwiftUI는 모든 Apple 플랫폼에 대한 사용자 인터페이스를 선언하는 현대적인 방법입니다.
그 어느 때보다 빠르게 아름답고 역동적인 앱을 만드세요.
라고 되어있네용
예상 시간은 4시간 25분 정도라는데 하나하나 깊게 파고들어서 하다 보면 더 소요될 것 같아요
처음엔 저도 엄청 빠르게 빠르게 따라 하면서 넘어갔는데 내용이 잘 안 들어와서 그냥 천천히 다시 해보기로 했어요
저는 SwiftUI를 먼저 써보고 나중에 튜토리얼을 봤는데 튜토리얼부터 해보고 시작할걸 그랬나 봐요..^^
은근 새롭게 알게된 점이 많았어요!
아무튼 이제 시작해보겠습니당
튜토리얼은 크게 5가지로 나뉘어 있네요
1장은 SwiftUI의 필수적인 부분을 알려주는 것 같아요.
"SwiftUI를 사용해 간단한 뷰에서 풍부한 뷰를 구성하고, 데이터 흐름을 설정하고,
Xcode의 preview(미리 보기)에서 펼쳐지는 것을 보면서 탐색을 구축하는 방법을 배울 수 있다" 하네요.
2장은 모양과 경로를 그리는 방법을 알아보고
view 간에 원활한 전환을 만드는 동시에 animation badge를 만들 수 있다고 하네요.
3장은 디자인 및 레이아웃 부분으로 SwiftUI로 구축된 더 복잡한 인터페이스의 구조와 레이아웃을 보여줍니다.
4장은 플랫폼별 UI프레임워크의 view 및 view controller와 함께 SwiftUI를 사용하는 예를 보여줍니다.
마지막은 SwiftUI에 관한 다양한 문서들이 모여있습니다.
오늘은 1장부터 살펴보도록 하겠습니당
SwiftUI Essentials
1. Creating and Combining Views
이번 튜토리얼은 좋아하는 장소를 발견하고 공유하기 위한 앱(Landmarks)을 구축하는 과정을 보여주고 있습니다.
랜드마크의 세부 정보를 보여주는 view를 구축하는 것으로 시작합니다.
view를 배치하기 위해 stack을 사용하여 이미지 및 텍스트 요소를 결합하고 계층화합니다.
view에 지도를 추가하기 위해선 MapKit을 포함합니다.
view의 디자인을 구체화할 때 Xcode는 실시간 피드백을 제공하므로 변경 사항이 코드로 변환되는 방식을 확인할 수 있습니다.
설명을 다 읽어봤으니 프로젝트 파일을 다운로드해줍니다!
Section 1
Create a New Project and Explore the Canvas
튜토리얼 대로 프로젝트를 생성해 줍니다.
App.swift 파일이 뭘까? 하고 봐보면
App 프로토콜을 준수하고 있는 것을 볼 수 있습니다.
공식문서를 자세히 들여다보면
이 프로토콜은 앱의 구조와 동작을 나타내는 타입이라고 되어있는데요
그리고 아래 설명을 보면
App 프로토콜을 따르는 구조체를 선언하여 앱을 만든다.
required body computed property(연산 프로퍼티)를 구현하여 앱의 콘텐츠를 정의한다.
라고 하는데
예..? 뭔 말일까요..
스크롤을 가장 아래로 내려봅니다..
App 프로토콜에서 body 프로퍼티를 필수 구현을 요구하고 있습니다.
따라서 쉽게 생각하면
App 프로토콜을 따르는 구조체를 선언해 앱을 만들 수 있고,
body computed property(연산 프로퍼티)를 구현해 앱의 콘텐츠를 정의한다!
그리고 body 속성은 하나 이상의 scene을 반환하며 차례로 표시할 콘텐츠를 제공하고,
각 scene은 view 계층 구조의 root view가 포함되어 있고 시스템에서 관리하는 생명주기를 가진다!
라고 생각하면 될 것 같네용
scene은 일반적으로 윈도우 화면에 콘텐츠를 나타냅니당
(더 자세한 부분은 포스팅하게 되면 링크 걸어두겠습니답..!)
아무튼 다시 본론으로 와서
App 프로토콜을 따르는 구조체를 선언하여 앱을 만든다.
required body computed property(연산 프로퍼티)를 구현하여 앱의 콘텐츠를 정의한다.
이제 이 말이 이해가 되시겠죠??!
이해가 안됐어도 아래 조금만 더 읽어보기로 해용
이어서
구조체의 선언 앞에 @main 속성은 App 특성을 사용하여
사용자 지정 프로토콜 준수자가 앱의 entry point(진입점)를 제공한다는 것을 나타냅니다.
모든 앱 파일 중에서 정확히 하나의 entry point만 가질 수 있습니당
이제 마지막..
WindowGroup 넌 뭐 하는 애일까
동일하게 구성된 window 그룹을 나타내는 scene이라고 하는데요
WindowGroup은 Apple의 모든 플랫폼에서 데이터 기반 애플리케이션을 구축하는 방법을 제공한다.
macOS, iPadOS와 같이 다중 윈도우 기능을 제공하는 플랫폼에서, 플랫폼 간에 코드를 더 쉽게 공유할 수 있도록 한다.
다시 정리해 보자면
App, Scene, View는 계층 구조를 형성하고 있고
View는 기본적인 구성 요소로서 화면에 표시되는 모든 것을 렌더링 하며, 더 복잡한 UI를 구성하기 위해 조합할 수 있다.
View는 Scene의 콘텐츠를 구성하며, 이를 통해 플랫폼에 독립적으로 표시될 수 있다.
View와 마찬가지로, 이러한 Scene들은 더 복잡한 Scene을 구성하기 위해 함께 조합될 수 있다.
이러한 모든 Scene들이 앱의 콘텐츠를 형성한다.
View가 Scene 구성 -> Scene이 모여서 -> App
라고 생각하면 쉬울 듯 하다!
SwiftUI의 핵심 구성요소를 알아보았으니 본격적으로.(?) 튜토리얼을 진행해 보겠습니다
Section 2
Customize the Text View
ContentView.swift 파일로 들어와봅니당
SwiftUI의 View 파일은 두 개의 구조를 선언한다.
첫 번째 구조는 View프로토콜을 준수하고 View의 콘텐츠 및 레이아웃을 묘사한다.
두 번째 구조는 해당 View에 대한 preview(미리 보기)를 선언한다.
preview가 보이지 않는다면
Canvas 켜주기!
preview가 정상적으로 켜졌다면
짜잔
그리고 여기서 Commmand + 컴포넌트 클릭하면
팝업이 뜬다는데 안 뜨길래 뭐지..? 하고 보니까
왼쪽 아래 하단에 화살표 표시 보이시져? 그거 눌러주시면 됩니당
아무튼 "Show SwiftUI Inspector" 선택해 주면
어머? 전 이런 기능이 있는지 몰랐어요
튜토리얼 진작에 해볼걸..(머쓱)
그리고 code editor 안에서도 똑같이 Command + 해당 컴포넌트 클릭
하면 인스펙터를 볼 수 있네용
SwiftUI preview는 수정사항을 자동으로 업데이트해줍니당
그래서 UI 짜기 더 수월한 것 같아요
Section 3
Combining Views Using Stacks
다음으로 stack을 사용해 View를 결합해 보겠습니다
순서대로 쭉쭉 진행해봅니당
진행하고 나면 이런 화면이 완성되는데요
코드상의 VStack, HStack은 뭘까?
VStack은 Vertical(수직) 정렬,
HStack은 Horizontal(수평) 정렬,
ZStack이라는 것도 있는데 이는 하위 view를 오버레이 하여 정렬합니다
따라 해보면서 알게 된 점이 있는데요
SwiftUI의 View는 정렬을 따로 지정해주지 않으면 가운데 정렬이 default 인 것 같더라구요?
VStack을 들어가 보니 정말 default 값으로 center가 지정되어 있었어요!
기본 매개변수로 초기화하면 stack view는 콘텐츠를 가운데 정렬하고 내재된 요소 사이에 약간의 간격(spacing)을 삽입한다.
view modifier, Spacer, Divider를 사용하여 stack을 결합하고 커스텀하면 유연하고 복잡한 레이아웃을 생성할 수 있다.
Spacer는 위의 코드에서와 같이 상위 뷰가 허용하는 만큼의 공간을 차지합니다.
Divider는 다른 콘텐츠를 구분하기 위한 구분선입니다.
일단 이렇게 간단하게만 알아보고 더 파보는 건 다른 포스팅에서..ㅎ(너무 길어져서..)
Section 4
Create a Custom Image View
이번엔 이미지 뷰를 커스텀해볼 거래요
다시 튜토리얼을 차근차근 진행해줍니당
Xcode assets에 jpg 이미지를 추가해 주고
SwiftUI View 파일을 생성해줍니당
View에 이미지를 띄우기 위해선 어떻게 해야 할까?
짜잔
이렇게 간단하게 가져올 수 있네요
그렇다면 이미지를 생성할 수 있는 방법은 위와 같이 이미지 이름만 가지고 생성할 수 있는 걸까?
하고 궁금해서 또 공식문서 파헤치러 가보기..
다양한 소스로부터 이미지를 생성할 수 있다고 하네용
앱의 asset 라이브러리 또는 bundle에 있는 이미지 파일(지원되는 유형은 PNG, JPEG, HEIC 등이 있음)
UIImage나 NSImage와 같은 플랫폼 별 이미지 유형의 인스턴스
Core Graphics CGImage 인스턴스에 저장된 비트맵
시스템 그래픽 SF Symbols set
이미지는 standard view modifier를 사용해 앱의 인터페이스에 맞게 크기를 조정할 수 있다.
그리고 아래 설명을 보면
"이미지는 late-binding 토큰이라서 이미지를 사용하려고 할 때만 실제 값을 확인한다"
라고 되어있어요
저는 여기서 late-binding 대충 감은 오는데 정확하게 알고 싶어서 찾아봤어요
(찾다가 chat gpt한테 물어봄)
오... 설명 한번 기깔나게 하네요
Image의 late binding은 SwiftUI의 선언형 프레임워크와 관련 있었네요
근데 chat gpt 설명보고 뭔가 이해가 되긴 했는데 느낌이 안 왔어요
그래서 더 찾아봤습니당..
선언형 프로그래밍은 코드가 '무엇'을 해야 하는지를 설명하는 반면,
명령형 프로그래밍에서는 코드가 '어떻게' 동작해야 하는지를 설명한다.
late binding이 선언형 프로그래밍과 밀접한 관련이 있는 이유는
SwiftUI가 뷰의 상태 변경을 자동으로 감지하고 UI를 업데이트하기 때문이다.
이는 @State와 @Binding 프로퍼티 래퍼를 사용해 이루어진다.
@State는 뷰의 상태를 나타내는 속성에 사용되며, 이 값이 변경될 때마다 뷰가 자동으로 다시 렌더링 된다.
@Binding은 한 뷰에서 다른 뷰의 @State 속성에 대한 참조를 제공한다.
이는 해당 속성의 값을 읽고 쓸 수 있음을 의미하며, 상태의 변화를 늦게 바인딩하게 된다.
따라서 사용자 상호작용이나 애플리케이션 상태 변화에 따라 UI를 동적으로 업데이트할 수 있다.
import SwiftUI
struct LateBindingExample: View {
@State private var isImageVisible = false
var body: some View {
VStack {
if isImageVisible {
Image("myImage")
.resizable()
.frame(width: 100, height: 100)
}
Button("Show Image") {
isImageVisible.toggle()
}
}
}
}
위 예시에서는 뷰가 생성될 때 이미지는 표시되지 않습니다.
이미지는 "Show Image"버튼을 눌렀을 때만 나타납니다.
여기서 이미지의 실제 내용은 버튼을 누를 때까지 볼 수 없습니다.
따라서 버튼을 누르고 isImageVisible이 true가 되면 시스템은 "myImage"라는 이미지 파일을 로드하고 해상도를 조정하여 보여줍니다.
이것이 late-binding 되는 예시입니당
위와 같이 SwiftUI는 사용자 상호작용이나 애플리케이션 상태 변화에 따라 UI를 동적으로 업데이트할 수 있다.
때문에 late-binding은 SwiftUI의 선언형 프로그래밍과 밀접한 관련이 있다.
뷰의 상태 변화나 화면 표시 등이 "필요한" 시점에서만 관련된 코드를 실행하기 때문에 성능과 리소스 관리 측면에서 유리하다.
고 볼 수 있을 거 같아요.
튜토리얼을 다시 따라가 줍니다.
따라 하다 보면 아래와 같은 화면이 완성되는데용
clipShape이 뭘까요!
모를 땐 또 공식문서를 털어보러 갑니다..
"이 뷰의 클리핑 모양을 설정합니다."
클리핑??? 이 뭔 말이지 하고 파파고에서 찾아봤더니
오호 잘라내는 모양을 설정한다는 말이군요!
clipShape을 사용해서 제공된 모양으로 뷰를 클리핑(잘라낸다?)합니다.
클립 모양을 뷰에 적용하면 shape에 의해 가려지는 뷰의 부분을 보존하는 동시에 뷰의 다른 부분을 제거할 수 있다.
클립 모양 자체는 보이지 않는다.
글로만 보면 이해가 잘 안 되니 전 차근차근 이것저것 만져봤어요
단순히 이미지만 가져오게 되면 아래와 같이 보여요
난 여기에 동그란 테두리를 넣고 싶어!
하고 overlay로 테두리를 넣어줬어요
띠용 0_0
Circle 테두리 밖에 남아있는 부분이 있네요..
아하! 근데 아까 clipshape를 사용하면
제공된 모양으로 뷰를 클리핑(잘라낸다)
클립 모양을 뷰에 적용하면 shape에 의해 가려지는 뷰의 부분을 보존하는 동시에 뷰의 다른 부분을 제거할 수 있다.
클립 모양 자체는 보이지 않는다.
라고 했으니
shape에 의해 가려지는 뷰의 부분 = 동그라미 부분
뷰의 다른 부분 = 테두리 밖 부분
가 되겠네요
때문에 겉의 테두리를 제거하려면 clipShape를 써야겠네용!
이렇게 clipShape를 써주고 shadow를 넣어주면 예제와 같은 화면이 됩니당
Section 5
Use SwiftUI Views From Other Frameworks
아직도 안 끝났어..? 싶지만
이번 챕터의 마지막 섹션이니 끝까지 화이팅!
마지막 섹션은 지정된 좌표를 중심으로 지도를 만들고, 이는 MapKit을 사용해 지도를 렌더링하나봐용
오 재밌겠다
그전에 전 물음표 살인마니까 또 알아보러 갈 거예요
MapKit 너는 뭐닝
앱 내에서 지도나 위성사진을 표시하고, 장소의 위도/경도를 포함한 위치 정보도 알 수 있다.
라고 보면 될 것 같아요
또 튜토리얼을 따라가줍니당
지도의 지역 정보를 담고 있는 private한 @State 변수를 만들어줍니다.
참고로 @State란 변수의 값이 변할 때 view를 다시 계산해서 그려주는 프로퍼티 입니다.
보통 해당 view 안에서만 사용되기 때문에 private와 함께 선언됩니다!
MKCoordinateRegion은 뭘까?!
MKCoordinateRegion은 struct로 특정 경도와 위도를 중심으로 둘러싼 사각형 모양의 지역범위를 표시하고 있다.
라고 되어있네요!
생성자를 자세히 들여다 봐보겠습니당
두 개의 매개변수를 가지고 있네요 하나씩 들여다 볼게용
CLLocationCoordinate2D은 위도 및 경도 데이터를 저장할 수 있는 것으로 보이고,
MKCoordinateSpan은 우리가 지정해주고자 하는 지역 범위의 폭과 너비를 정해줄 수 있는 struct입니다.
지도의 zoom level을 정할 수 있네요!
value가 작아질수록 higher zoom level이 된다고 하니 값이 작을수록 더 확대돼서 보인다고 생각할 수 있습니다.
한마디로 우리는 이 경도와 위도 중심으로
지도의 zoom level은 각각 0.2 정도로 사각형 모양의 지역 범위를 표시할 거야
private @State로 선언했으니 해당 view안에서만 사용할 거고 이 값이 변하면 view를 다시 그려줄 거야
라고 생각하면 되겠네요
그리고 body에 선언된 Map은 내장된 지도 인터페이스를 표시하는 view입니다.
Map은 body 내부에, region은 구조체 내부에 선언되어 있기 때문에
swift에서 구조체 내부의 값을 변경하기 위해서는 body 프로퍼티 앞에 mutating 키워드를 사용해야 합니다.
하지만 View 프로토콜에 정의된 body 프로퍼티는 get only 즉, 읽기 전용으로 정의되어 있습니다.
따라서 body 프로퍼티 내부에서 region 변수의 값을 수정할 수 없습니당
이럴 때 사용할 수 있는것이 @State 속성으로 Map View에서 region 변수의 값을 변경 가능하도록 하며
SwiftUI는 @State로 선언된 변수의 값이 변경되는 것을 인식하고 값이 변경될 경우 view를 자동으로 업데이트해줍니다.
근데 특이하게 변수 앞에 $가 붙어있죠?
@State 변수 앞에 를 추가하면 $기본 값에 대한 참조와 유사한 binding을 전달합니다.
이것은 region 변수에 대한 binding으로 지도의 좌표 값이 변경되면
@State로 선언된 wappered property의 값을 변경하여 view를 업데이트합니다.
Section 6
Compose the Detail View
이젠 필요한 요소들이 모두 완성 됐으니 Detail View를 구성해 보는 시간을 가져보겠습니당
튜토리얼을 따라 해 줄게요!
이 부분에선 딱히 어려운 점은 없던 것 같아요
여기서 눈여겨볼 점 하나 뽑자면 MapView에 쓰인 ignoreSafeArea인 것 같아요
ignoreSafeArea는 가장자리까지 view가 확장되도록 하는 함수인데요
따로 지정해주지 않으면 기본적으로 모든 안전 영역 가장자리를 무시하고 모든 방향으로 확장되는 새 보기를 반환합니다.
Tutorial 예시와 같이 top부분만 확장하고 싶다면
이렇게 작성해 주면 되겠네요!!
만약 두 개 이상 확장하고 싶다면 아래와 같이 배열로 넘겨주면 됩니당
우여곡절 끝에 드디어 끝났네요!!!
단순히 따라 하기만 하면 정말 빨리 끝날 거예요
하지만 이렇게 하나하나 핥으면서(?)하니 SwiftUI가 어떻게 흘러가는지 감이 오는 거 같아요
다음번엔 Building Lists and Navigation 부분으로 찾아오겠습니다람쥐🐿️
'iOS > SwiftUI' 카테고리의 다른 글
[SwiftUI] 데이터 바인딩 - EnvironmentObject, StateObject (2) (0) | 2023.04.29 |
---|---|
[SwiftUI] 데이터 바인딩 - State, Binding, ObservedObject (1) (0) | 2023.04.05 |