view
는 state의 결과로 유저 인터페이스를 구성함UI window
는 view들을 담는 컨테이너form
에 modifier
를 추가하면 form
의 새로운 카피가 생성됨프로퍼티 래퍼
struct ContentView: View {
@State var tapCount = 0
var body: some View {
Button("Tap Count: \(tapCont)" {
self.tapCount += 1
}
}
}
two-way binding
이 되는 경우 양자가 서로 영향을 받음$
를 붙여서 two-way binding
임을 나타낼 수 있음 struct ContentView: View {
@State private var name = ""
var body: some View {
Form {
TextField("Enter your name", text: $name)
Text("Your name is \(name)")
}
}
}
처음에는 grandTotal
변수만 따로 선언해서 totalPerPerson
변수 연산 과정 중에 값을 지정해줄 수 있지 않을까 했는데 이 방법은 불가능했다
원인 : 렌더링 중 grandTotal 변수가 변경되어 다시 렌더링을 요청하게 되므로 SwiftUI가 혼란에 빠짐!
다양한 방법을 고민하다가 그냥 아예 grandTotal
변수와 그와 관련된 일련의 연산 과정을 별도의 연산 프로퍼티로 정의해서 해결했다
var grandTotal : Double {
let tipSelection = Double(tipPercentages[tipPercentage])
let orderAmount = Double(checkAmount) ?? 0
let tipValue = orderAmount * tipSelection / 100
let totalAmount = orderAmount + tipValue
return totalAmount
}
var totalPerPerson : Double {
let peopleCount = Double(numberOfPeople) ?? 0
let amountPerPerson = grandTotal / peopleCount
return amountPerPerson
}
Picker
부분 코드를 TextField
로 바꿔줬는데 다음과 같은 2가지 문제가 있었다 TextField("Number of People", text: $numberOfPeople)
.keyboardType(.numberPad)
TextField
는 기본적으로 입력을 문자열 형태로 받는데 numberOfPeople
을 Int로 초기화했다 : 이 부분은 초기화를 빈 문자열로 바꿔줘서 해결 @State private var numberOfPeople = ""
totalPerPerson
을 구하기 위해 이제 numberOfPeople
을 형변환하면 옵셔널 더블
이 되므로 nil coalescing
이 필요했는데 별 생각없이 nil
일 때의 디폴트 값을 0으로 설정했더니 아마도 ZeroDivisionError
가 발생했는지 아무것도 입력하기 전 앱을 처음 킨 상태에서 NaN
이 떴다... : 디폴트 값을 1
로 바꿔서 해결! let peopleCount = Double(numberOfPeople) ?? 1
body
가 하나의 프로퍼티라니...다 작성하고 짚어줘서 알았다...프로퍼티에 대한 고정관념이 있었던 것 같다...결국 하나의 view를 리턴하므로 하나의 연산 프로퍼티라는 점이 아직 엄청 와닿지는 않지만 계속 해보다보면 익숙해지겠지 싶은 마음...!TextField
는 항상 문자열을 받으므로 다른 타입으로 사용하고 싶다면 형 변환
이 필요하다.navigationBarTitle()
modifier 와 관련해서 NavigationView
가 끝나는 시점에 써야되는 게 아닌가 했는데, 그 내부의 Form
이 끝나는 시점에 attach 해야 navigation view 가 여러개의 view를 계속 보여줄때 제목을 자유롭게 변경할 수 있다고 한다...