You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(장점) 모든 뷰를 explicity하게 식별할 필요가 없고 헤더 텍스트와 같이 코드의 다른 곳에서 참조해야하는 뷰만 식별하게 할 수 있다.
ScrollViewReader{ proxy inScrollView{HeaderView(rescueDog).id(headerID)Text(rescueDog.backstory)Button("Jump to Top"){withAnimation{
proxy.scrollTo(headerID)}}}}
이 때 headerView만 id를 가진다고 다른 view들이 identity를 안 가지는 것은 절대 아니다. 모든 뷰는 identity를 가지고 있고, 이것이 explicit이 아니어도 된다.
여기서 structural identity의 개념이 나온다.
Structural identity
뷰 계층 구조에서 유형과 위치에 따라 구분하는 identifier
비슷한 개가 두마리 있지만 이름을 모를 때, 위치를 기반으로 그들을 식별가능하다.
즉, 상대적인 위치, 배열을 사용하여 View를 구별하는데 이를 structural identity라고 한다.
SwiftUI는 전체 코드에서 structural identity를 활용한다.
가장 기본적인 예제는 if 문과 같이 조건부 논리를 사용하는 예이다.
조건부 구조는 이 identity를 확실하게 줄 수 있다.
// ✅조건이 참일 때 표시되는 View와 조건이 거짓일 때 표시되는 View
varbody:someView{if rescueDogs.isEmpty {AdoptionDirectory(selection: $rescureDogs)}else{DogList(rescueDogs)}}
하지만 이것은 SwiftUI가 뷰가 제자리에 유지되고 절대 위치를 바꾸지 않는 정적인 경우에만 작동한다.
그러면 보장되지 않을 경우엔???
뷰 계층 구조의 타입 구조를 살펴본다.
이 말은 즉 SwiftUI는 뷰를 볼 때, 제네릭 타입으로 본다.
some View가 Generic 타입으로 ViewBuilder에 의해 빌드될 때 변형된다. View 프로토콜은 body 프로퍼티를 ViewBuilder안에 암시적으로 감싸져 있다. 그래서, some View는 이 정적 복합 유형을 나타내는 자리 표시자로 코드를 복잡하지 않게 숨겨준다.
**영상에서 소개한 선호하는 identity**
SwiftUI는 이 두가지 identity 모두 선호하지만, structural identity보다 explicity identity를 사용하는 것을 더 선호한다. 하나의 View를 분기문으로 나누게 된다면 서로 다른 고유 ID를 가진 다른 뷰이기 때문에 애니메이션이 `in and out`뿐이다. 하지만 일관된 ID로 단일뷰를 수정하게 된다면 애니메이션이 부드럽게 이루어지는 것을 알 수 있다.
AnyView
"타입 지우기 래퍼 타입", 즉 뷰 타입을 숨겨준다.
SwiftUI가 코드를 볼 때 조건 분기는 제네릭 유형 구조로 보게된다.
만약 함수의 각 조건 분기에서 다른 종류의 뷰들을 반환하는 경우 swift는 전체 함수에 대해 단일 반환 유형이 필요하기 떄문에 AnyView로 모두 래핑한다.
이럴 경우 SwiftUI는 코드의 조건부 구조를 볼 수 없고 AnyView를 함수의 반환 유형으로 본다.
모든 조건의 반환 타입에 AnyView가 붙게 될 경우 코드가 복잡해진다.
그러므로 @ViewBuilder를 붙임으로 해결이 가능하다.
// 예제 코드 삽입
일반적으로 AnyView를 피하는 것이 좋다.
AnyView가 너무 많으면 코드를 읽고 이해하기가 어려워진다.
AnyView는 컴파일러가 정적 유형 정보를 숨기기 때문에 오류를 찾기가 더 힘들어진다.
필요하지 않은 경우에 AnyView를 사용하면 성능이 저하될 수 있다. 그러므로 가능하면 제네릭을 사용하여 정적 유형 정보를 유지해라.
Lifetime
이제는 뷰의 ID가 뷰와 데이터의 수명과 연결되는 지 살펴보자.
View Lifetime
이름과 같은 구분자(identifier)를 통해 시간의 흐름에 따라 다른 값을 가지는 안전적인 요소를 정의할 수 있다.
아래 예제에서 하나의 Element인데 그 내부의 Value는 계속 달라지는 것을 확인할 수 있다.
View의 value는 뷰의 identity와 다르다.
즉, View의 value(값)는 일시적이므로 수명에 의존해서는 안된다.
뷰가 처음 생성되고 나타날 때, SwiftUI는 뷰에 identifier를 할당한다.
시간이 지남에 따라 View에 새로운 value가 할당되지만 SwiftUI의 관점에서는 동일한 View이다.
뷰의 ID가 변경되거나 뷰가 제거되면 해당 수명이 종료된다.
즉, View의 생명주기는 해당 View의 identity의 지속 시간이다.
Data-driven constructs
SwiftUI는 데이터의 ID를 View의 explicit identity로 사용하는 몇 가지 데이터 기반 구조를 가지고 있는데 대표적인 예는 아래 그림이다.
ForEach 예시
일반적으로 일정 범위를 필요로 하며, 이 고정된 범위를 통해 View의 생명주기 동안 ID가 안정적임을 보장한다.
이 때, 동적 범위를 지정하는 것은 오류이며, 일정하지 않은 범위를 지정하면 경고가 표시된다.
동적 컬렉션일 경우 컬렉션과 키 경로를 ForEach의 입력으로 사용한다.
SwiftUI는 컬렉션의 요소를 통해 생성된 모든 뷰에 ID를 할당하기 위해 해당 값이 해시가 가능해야 한다.
이 때 표준 라이브러리의 Identifiable 프로토콜을 제공한다.
Swift는 위 프로토콜을 활용하여 ForEach에 키 경로를 생략하고도 데이터와 View의 ID를 연결할 수 있다.
정리
SwiftUI의 View의 value는 View Lifecycle에 의존하지 않지만 Identity의 지속 시간이다.
State와 StateObject의 지속성은 view의 lifecycle에 의존하지 않게 만든다.
SwiftUI는 데이터 기반 구성요소에 대해 Identifiable 프로토콜을 최대한 활용하여 데이터에 대한 안정적인 identity를 선택하는 것이 중요하다.
Dependency
SwiftUI가 UI를 업데이트하는 방법에 대해 살펴보자.
두 가지 프로퍼티는 Dependency라고 하며 View에 대한 input이다.
Dependency가 변경되면 View는 새로운 body를 요구한다.
body는 View의 계층을 구성한다.
Action은 View의 Dependency에 대한 변경을 일으킨다.
위 DogView 코드를 다이어그램으로 그려보자.
위는 트리 형태의 구조이지만, 각 View마다 Dependency를 걸게된다면 아래와 같이 graph의 형태로 변하게 된다.
Dependency Graph
위 다이어그램을 다시 그려보면 아래와 같이 표현할 수도 있다. 이를 Dependency Graph라고도 한다.
이 그래프의 하단 dependency가 변경이 되었다고 가정한다.
맨 아래에 있는 항목이 수정되면 연결된 두 개의 뷰만 업데이트가 필요하다.
따라서 SwiftUI는 각 View의 body를 호출하여 갱신한다.
이후 SwiftUI가 변경이 필요한 뷰만 효율적으로 업데이트한다.
정리
즉, ID(identity)는 Dependency Graph의 핵심이다.
모든 View는 ID를 통해 동일성을 체크하고, 이를 통해 SwiftUI가 변경 사항을 올바른 View에 효과적으로 적용한다.
아래는 많은 종류의 dependency 예제이다.
// ✅ Properties of the struct
@Binding@Environment@State@StateObject@ObservedObject@EnvironmentObject
Identifier
Identity, ID 등 View의 구분자를 의미한다. 위에서 계속 설명했던 것인데 용어가 여러개 나오네요..
좋은 Explicit Identifier
Stable (안전성) : 매번 바뀌면 안된다.
애니메이션 개선
Unique (고유성)
애니메이션 개선
좋은 성능
계층 구조의 의존관계가 효율적으로 유지
좋은 Structure Identifier
불필요한 분기는 자제하자.
단일 View를 위해서는 분기 대신 수정자를 사용하는 것이 더 잘 작동한다.
The text was updated successfully, but these errors were encountered:
Demystify SwiftUI
SwiftUI는 선언형 프레임워크로, 앱에 대해 원하는 것을 높은 수준에서 작성하면 SwiftUI가 이를 실행하는 방법을 결정한다. 이는 정말 마법처럼!! 엄청나다고 느껴질 수 있다.
하지만, SwiftUI가 예상치 못한 일을 할 때가 있다. 그 순간에 SwiftUI가 원하는 결과를 얻는 방법에 대한 더 나은 이해를 위해 scene 뒤에서 무엇을 하는 지 아는 것이 중요하다.
이 영상은 **SwiftUI가 코드를 볼 때 무엇을 보는가?**에 대한 답으로 세가지를 소개한다.
Identity
Swift는 identity로 View들을 구분한다. 먼저, SwiftUI에서 사용하는 두 가지 identity에 초점을 맞춰 코드에 어떻게 보여지는 지 살펴보자.
Explicit identity
그렇다면 View in SwiftUI는 어떻게 identity를 가질 수 있냐!
dogTagID
부여)id(_:)
modifier를 이용해 커스텀 identifier 제공하는 방법이 때
headerView
만 id를 가진다고 다른 view들이 identity를 안 가지는 것은 절대 아니다. 모든 뷰는 identity를 가지고 있고, 이것이 explicit이 아니어도 된다.여기서 structural identity의 개념이 나온다.
Structural identity
하지만 이것은 SwiftUI가 뷰가 제자리에 유지되고 절대 위치를 바꾸지 않는 정적인 경우에만 작동한다.
그러면 보장되지 않을 경우엔???
뷰 계층 구조의 타입 구조를 살펴본다.
이 말은 즉 SwiftUI는 뷰를 볼 때, 제네릭 타입으로 본다.
**영상에서 소개한 선호하는 identity** SwiftUI는 이 두가지 identity 모두 선호하지만, structural identity보다 explicity identity를 사용하는 것을 더 선호한다. 하나의 View를 분기문으로 나누게 된다면 서로 다른 고유 ID를 가진 다른 뷰이기 때문에 애니메이션이 `in and out`뿐이다. 하지만 일관된 ID로 단일뷰를 수정하게 된다면 애니메이션이 부드럽게 이루어지는 것을 알 수 있다.
AnyView
AnyView
가 붙게 될 경우 코드가 복잡해진다.@ViewBuilder
를 붙임으로 해결이 가능하다.일반적으로
AnyView
를 피하는 것이 좋다.Lifetime
이제는 뷰의 ID가 뷰와 데이터의 수명과 연결되는 지 살펴보자.
View Lifetime
Data-driven constructs
ForEach 예시
Identifiable
프로토콜을 제공한다.정리
State
와StateObject
의 지속성은 view의 lifecycle에 의존하지 않게 만든다.Dependency
SwiftUI가 UI를 업데이트하는 방법에 대해 살펴보자.
위
DogView
코드를 다이어그램으로 그려보자.위는 트리 형태의 구조이지만, 각 View마다 Dependency를 걸게된다면 아래와 같이 graph의 형태로 변하게 된다.
Dependency Graph
정리
Identifier
Identity, ID 등 View의 구분자를 의미한다. 위에서 계속 설명했던 것인데 용어가 여러개 나오네요..
좋은 Explicit Identifier
좋은 Structure Identifier
The text was updated successfully, but these errors were encountered: