TIL(Today I Learned)
12월 5일(일)
학습내용
- SwiftUI(CS193p Lecture 5)
- View는 Read-Only이다.
- Read-Only이므로 외부에서 변환시킬일이 없으므로 View의 프로퍼티는 모두 상수여야 한다. (@State, @ObservedObject를 제외하고)
- Stateless
- @State
- @State 는 View의 상태를 나타내기 위해 사용한다.
@State private var somethingTemporary: SomeType // SomeType은 struct이여야 한다.
- 접근 제한은 private이여야 한다. (View 내부에서만 사용되어야 하므로)
- 또한 변수의 타입은 struct type이여야 한다.
- @State의 변수 값이 변하면 뷰를 재생성한다.
- @State의 변수는 View의 lifetime 동안 heap에 살아있다.
- 여기서 lifetime이란, View가 화면에 나타나 있는 동안을 의미한다. (View struct의 lifetime이 아닌)
- View가 재생성되면 State 변수는 heap에 있는 내용을 다시 point 하게 된다.
- @State은 “source of truth”이다.
- @State 사용을 너무 남용하면 안된다.
- Model에 포함되지 않아야 한다.
- ViewModel
- View와 model 사이의 gatekeeper이다.
- ViewModel은 View에 보여지고 싶은 model을 노출시킨다.
- ViewModel의 Intent Function을 통해서만 model을 변경할 수 있다. (Intent Function을 통해서 사용자와 상호작용)
- View
- View 내부에 non-private var와 func을 선언하는 경우는 드물다. (View가 ephemeral하므로, 즉 오래동안 지속되지 않으므로)
- Access Control
- open과 public은 주로 library code에서만 사용된다.
- internal이 기본이며, app 내부에서 접근이 가능하다.
- static 함수도 private이 가능하다. (private static)
- Xcode 변수명 변경방법
- 변경하려는 변수명을 command 키와 같이 누르면 말풍선이 뜨는데, rename을 클릭한다.
- Computed Property
- Computed Property 내부에 get {}, set {}을 구현할 수 있다.
-
set 내부에서 입력 값을 newValue로 참조할 수 있다.
private var indexOfTheOneAndOnlyFaceUpCard: Int? { get { cards.indices.filter({cards[$0].isFaceUp }).oneAndOnly } set { cards.indices.forEach { cards[$0].isFaceUp = ($0 == newValue) } } }
- Property Observers
- struct 변수에 할당된 값의 변화를 감지하기 위한 것
- willSet, didSet
- willSet - newValue
- didSet - oldValue
- Layout
- SwiftUI는 뷰에 어떻게 공간을 할당하는가? (뷰 크기 결정 과정)
- Container View은 하위 뷰에 space을 전달한다.
- View는 자기 자신의 크기를 스스로 결정한다.
- Container View는 크기가 결정된 하위 뷰를 적절하게 위치시칸다.
- 위 과정으로 결정된 크기로 Container View의 크기를 결정한다.
- HStck, VStack
- 하위 뷰 중 제일 flexible하지 않은 뷰에 먼저 space을 제공한다.
- Image(inflexible) : 고정된 크기로 존재하려 한다.
- Text(slightly more flexible) : 텍스트에 맞는 크기로 존재하려 한다.
- RoundedRectangle(very flexible) : 남은 공간을 차지한다.
- 가장 유연하지 않은 뷰부터 공간을 전달하면서 크기를 결정하고, 이후 남은 공간을 다음 유연한 뷰에 전달하는 방식으로 공간이 할당된다. (가장 유연한 뷰가 제일 마지막에 나머지 공간을 차지한다)
- 내부의 뷰의 크기가 모두 결정되면, 그것들을 감싸는 크기로 Stack의 크기가 결정된다.
- 만약 스텍 내부에 존재하는 뷰가 모두 very flexible일 경우, 스텍도 마찬가지로 very flexible이 된다.
- Spacer(minLength: CGFloat)
- 아무것도 그리지 않는 뷰. 나머지 모든 공간을 차지한다. 빈공간을 만들기 위해 사용
- Divider()
- 스텍 내부에 구분선을 만든다. 선을 그리기 위한 최소한의 공간을 차지.
- .layoutPriority(Double)
- 어떤 뷰가 공간을 전달받을것인지를 이 우선순위를 통해 바꿀 수 있다. 숫자가 높을수록 우선순위가 높다.
- alignment
- 스텍 내부에서 뷰를 어떻게 정렬할 것인가를 결정.
- .leading vs .left
- left는 절대적 위치(왼쪽) leading은 기기 언어설정에 맞게 왼쪽 또는 오른쪽이 될 수 있다. (Arabic 또는 Hebrew)
- 하위 뷰 중 제일 flexible하지 않은 뷰에 먼저 space을 제공한다.
- LazyHStack, LazyVStack
- onscreen이 아닌 뷰는 그리지 않는 스텍이다.
- scrollview내부에 스텍을 넣고 싶을 때 사용한다.
- 내부에 flexible만 있어도 스텍이 flexible해지지 않는다.
- ScrollView
- LazyHGrid and LazyVGrid
- List and Form and OutlineGroup
- Smart VStack
- ZStack
- 겹쳐지는 뷰
- 자식뷰에 의해 사이즈가 결정된다.
- 자식이 flexible이면 ZStack도 flexible해진다.
- .background modifier
- 겹쳐진다는 면에서 ZStack과 비슷하지만, 크기가 뷰에 종속적이라는점이 차이점이다.
- Text(“hello”).background(Rectangle().foregroundColor(.red))일 경우, 사각형 크기는 텍스트의 크기와 동일하다.
- .overlay modifier
- 마찬가지로 뷰를 겹치기 위해서 사용된다. (앞에다가 둔다)
- Circle().overlay(Text(“Hello”), alignment: .center)일 경우, 원 앞면의 중앙에 원 크기와 동일한 크기의 텍스트가 들어가게 된다.
- Modifiers
- view를 반환한다는 점에서 container view와 비슷하다.
- 원래 뷰의 크기 그대로의 뷰를 반환한다.
- padding
- 반면에 padding은 뷰의 레이아웃 변화에 관여한다고 할 수 있는데, 여백을 넣고 원래 뷰의 크기를 줄이기 때문이다.
- aspectRatio
- 뷰의 가로 세로 비율을 정하기 위한 기능
- 전달받은 공간보다 더 큰 크기(.fill) 또는 더 작은 크기로 비율을 적용할 수 있다 (.fit)
-
Example(01:03:20 - 01:03:30 )
HStack { ForEach(viewModel.cards) { card in CardView(card: card).aspectRatio(2/3, contentMopde: .fit) } } .foregroundColor(Color.orange) .padding(10)
- 공간을 전달 받을 첫번째 뷰(상위 뷰)는 padding 10이 적용된 뷰이며, 상하좌우 모든 면이 10이 감소되고 forgroundColor가 적용된 뷰가 될 것이다.
- 뷰의 전체 공간을 먼저 HStack에 전달한다
- ForEach로 생성된, 그리고 aspectRatio가 적용된 뷰에게 HStack이 전달받은 공간을 나누어 전달한다.
- aspectRatio가 적용된 뷰는 전달받은 공간의 width를 본인의 width로 취하며, 2/3 비율에 맞게 height를 결정한다. (또는 반대로, 높이를 먼저 결정하고 비율에 맞게 너비를 결정)
- 결국 전체 뷰의 크기는 HStack이 결정한 크기(.aspectRatio 뷰들의 크기 + 10)가 된다.
- GeometryReader
- 전달받은 모든 공간을 사용하며, 현재 뷰의 공간의 크기를 알고 싶을 떄 사용한다.
- ViewBuilder 이며, content에서 proxy를 argument로 전달받는다.
- SafeArea
- 아이폰 X 노치
- .edgesIgnorigSafeArea()로 무시할 수 있다.
-
View 내부에 아래와 같은 방식으로 뷰에서 사용할 상수를 저장할 수 있다.
struct CardView: View { private struct DrawingConstants { static let cornerRadius: CGFloat = 20 ... } }
- @ViewBuilder
- 기존의 클로저는 swift 코드의 실행을 의미했는데, 이를 뷰의 나열로 바꾸기 위한 문법이다.
- 함수나 연산 프로퍼티에 @ViewBuilder라고 기재하면 된다.
- if-else로 조건에 따라 뷰를 변경하는 것이 가능하다.
- let 사용이 가능하다.
@ViewBuilder func front(of card: Card) -> some View { let shape = RoundedRectangle(cornerRadius: 20) shape shape.stroke() Text(card.content) }
- SwiftUI는 뷰에 어떻게 공간을 할당하는가? (뷰 크기 결정 과정)
- View는 Read-Only이다.
참고