---
# System prepended metadata

title: Introduction to SwiftUI (wwdc)

---

# Introduction to SwiftUI (wwdc)

- VStack
    - 세로로 쌓을 수 있음
- HStack
    - 가로로 쌓을 수 있음
- 뷰에 마우스를 대고 cmd+클릭 => Embed in HStack 하면 HStack으로 감싸짐
- 뷰에 있는 요소에 대고 cmd+클릭 => inspector로 이동 
- wwdc에서는 스와이프가 된다고 하는데 나는 오ㅐ안됨..10:50
- 뷰에 cmd+클릭하고 Extract View 클릭하면 바로 뷰를 추출할 수 있음

![](https://hackmd.io/_uploads/Hky519hSh.png)

- 지금까지 우리는 single collection을 사용하여 목록을 만들었고, 이는 데이터에서 기반한 list였다.
- 하지만 SwiftUI를 사용하면 list에서 정적 content와 동적 content를 혼합하여 사용할 수도 있다.
=> 바로 ForEach를 사용하면 된다.

1. 처음
![](https://hackmd.io/_uploads/H1bNgcnS2.png)

2. ForEach 사용시

![](https://hackmd.io/_uploads/r1rPg93Sn.png)

이제 아래와 같이 목록 바깥에다가 정적 요소를 추가할 수 있게 됐다.

![](https://hackmd.io/_uploads/SJ0--92Hh.png)

![](https://hackmd.io/_uploads/Bkdzb9hBn.png)

- Spacer()은 SwiftUI의 일반적인 layout 요소이다. toolbar에서 유연하게 공간을 이동하며 사용 가능한 공간을 채우는 역할을 한다. 


#### SandwichDetail (이미지)

- resizable
    - SwiftUI가 공간에 맞게 이미지 크기를 조정하는 모드

- aspectRatio
    - 가로 세로 비율을 유지하며 화면에 맞도록 하는 모드
    - fill
        - 이미지 잘리기도 함 + 화면에 아예 꽉차게
        - ![](https://hackmd.io/_uploads/rk46V52rh.png)

    - fit
        - 이미지 전체를 보여줌 + 비율 맞춤 (화면에 꽉차지 않음)
        - ![](https://hackmd.io/_uploads/r11C452B3.png)


차례대로
![](https://hackmd.io/_uploads/SJQDa52Hn.png)
![](https://hackmd.io/_uploads/Hyrvp9nH3.png)
![](https://hackmd.io/_uploads/S1_vachr3.png)


#### 프리뷰에서 네비게이션뷰 적용하기


![](https://hackmd.io/_uploads/ByjqH92Bn.png)

원래 이랬는데 프리뷰에 NavigationView만 넣어주면 아래와 같이 제대로 나옴

![](https://hackmd.io/_uploads/HksjHc2B2.png)


#### 발표자의 질문 
> 샌드위치 안의 소스의 양을 확인하기 위해 이미지를 확대했다 작게했다 하면서 동적으로 컨텐츠를 변경하고싶은데 어떻게 할 수 있을까?
>  => 알려면 SwiftUI에서 뷰가 작동하는 방식과 이유에 대해 알아야한다.

- 스유에서 View는 UIView와 같은 기본 클래스에서 상속되는 클래스가 아니라 View 프로토콜을 준수하는 구조체이다. 
- 따라서 이는 뷰가 저장 프로퍼티를 상속하지 않음을 의미하고, 이는 스택에 할당되고 값으로 전달된다. (pass by value)
- 스유에서는 뷰가 엄청나게 light하다. => SwiftUI에서는 리팩토링을 망설일필요가 없다. subview를 추출하는 것은 런타임 오버헤드가 없기 때문이다.
- 프레임워크는 언제 다시 렌더링을 할지 알고있다. 그 이유는 view가 dependencies를 가졌기 때문이다. ‼️
- 사용자가 zoom하면 이미지를 fill로, 아니면 이미지를 fit으로 바꾸는 예시

![](https://hackmd.io/_uploads/B1nAGshS3.png)


- SwiftUI는 해당변수가 read, written 될 때를 observe할 수 있다.
- SwiftUI는 body내에서 zoomed가 read 되어지는것을 알고, 그것에 따라 렌더링하도록 할 수 있다.
- 즉, 해당 프로펕티가 변할떄, framework는 body가 다시 렌더링하도록 한다.


다시 돌아와서  아래 모서리부분에 빈 공간이 생기는 문제점이 발생했다. 

![](https://hackmd.io/_uploads/SkJrminBh.png)

- 이를 해결하기 위해서는 `edgesIgnoringSafeArea(.bottom)` 을 사용해주면 된다. 그럼 아래쪽 가장자리가 사라진다.

- Spacer()는 자동으로 요소 사이의 일부 padding을 유지하기 위한 최소한의 크기를 갖는다.
    - Spacer(minLength:)를 통해 설정할 수 있다.


- ![](https://hackmd.io/_uploads/rygOLinH3.gif)

이런 애니메이션이 자연스럽게 나오도록 WithAnimation과 transition을 사용해줌

![](https://hackmd.io/_uploads/r1eFUjhHh.png)
![](https://hackmd.io/_uploads/BJYFLinHh.png)



그럼 지금까지 아이폰에서 봤고, 아이패드에서는 어떻게 보일까?

아래와 같이 보인다.
![](https://hackmd.io/_uploads/S17CDi3Bn.png)
![](https://hackmd.io/_uploads/SJ4nvs2r3.png)


![](https://hackmd.io/_uploads/rye-OihH2.png)


그래서 빈 화면에 샌드위치를 선택하라는 글을 써주려면 이렇게 네비게이션뷰 안에 Text만 추가해주면 된다. 

![](https://hackmd.io/_uploads/BkMVOj3Sn.png)


### SandwichStore로 리팩토링🔩


1. 최상단 

![](https://hackmd.io/_uploads/rkIZKT3H3.png)

앱의 시작점은 @main을 통해 이루어지고, 그 객체는 App 프로토콜을 채택하며  WindowGroup에서 시작화면을 정의한다.

2. ContentView

![](https://hackmd.io/_uploads/BkzztTnr3.png)

3. SandwichStore

![](https://hackmd.io/_uploads/BkfXYT3H3.png)



### 📚 스유 프로퍼티 래퍼 정리 

> @ObservedObject / @Published 

@State는 `특정 view에서만 사용하는 프로퍼티` 였다면,
@ObservedObject는` 여러 view에서 공유할 수 있는 커스텀 타입이 있는 경우나 여러 프로퍼티나 메서드가 있는 경우`, 즉 **복잡한 프로퍼티**에 사용된다.

<br>

특징
> ❗️ 1. String이나 integer 같은 간단한 로컬 프로퍼티 대신 외부 참조 타입을 사용한다는 점을 제외하면 @State와 매우 유사.
> ❗️ 2. @ObservedObject와 함께 사용하는 타입은 ObservableObject 프로토콜을 따라야 함.
> ❗️ 3. @Observed object가 데이터가 변경되었음을 view에 알리는 방법은 여러 가지가 있지만 가장 쉬운 방법은 @Published 프로퍼티 래퍼를 사용하는 것. = SwiftUI에 view reload를 트리거.

예시
```swift
class UserSettings: ObservableObject {
   // @ObservedObject를 사용하기위해 @Published를 할당
   @Published var score = 0
}
 
struct ContentView: View {
    // @state를 지우고 @ObervedObject로 바꿔줌
    // view가 외부 객체를 감지하게 해주는 역할
    @ObservedObject var settings = UserSettings()
 
    var body: some View {
        VStack {
            Text("나의 점수는 \(settings.score)점 입니다.")
            Button(action: {
                self.settings.score += 1
            }) {
                Text("Increase Score")
            }
        }
    }
}
```
score에 @Published가 붙었기 때문에 이 score가 변경되면 view를 reload하게 된다.
 
 
> 참조타입이 아닌 struct로 해주고 싶을 때 
```swift
struct UserSettings {
    var score = 0
}
 
struct ContentView: View {
 
    @State var settings = UserSettings()
 
    var body: some View {
        VStack {
            Text("나의 점수는 \(settings.score)점 입니다.")
            Button(action: {
                self.settings.score += 1
            }) {
                Text("Increase Score")
            }
        }
    }
}
```

> @StateObject

WWDC 2020에서 애플은 @StateObject를 추가로 공개했다.
@ObservedObject와 거의 같은 방식으로 작동한다.
SwiftUI가 View를 다시 랜더링 할 때, 실수록 취소되는 것을 방지해준다고 한다.
