I want to understand if I got this concept correctly.
- I should start work from
ViewState; a reference type object holding the state of a view. Here it is calledPageIndicatorVM. It is bad-to-impossible to have any internal state in aView. - No logic in the
Viewor so they say. Yet here and there in the code samples I see ternary operators and loops. @Bidindgproperty wrapper seems quite dangerous for me. It breaks unidirectional data flow and I have doubts if I used it correctly here.
Please point out my mistakes. Will appreciate if you point me to best practices.
This is a dot indicator component. It switches when vm.next() is called or user taps on a dot.
struct DotIndicator: View {
let pageIndex: Int
@Binding var isOn: Int
var body: some View {
Button(action: {
self.isOn = self.pageIndex
}) {
Circle()
.scaleEffect( isOn == pageIndex ? 1.3 : 0.9)
.animation(.spring())
}
}
}
class PageIndicatorVM: ObservableObject {
@Published var currentPage: Int
let numPages: Int
init(numPages: Int, currentPage: Int = 0) {
self.numPages = numPages
self.currentPage = currentPage
}
func next() {
currentPage = (currentPage + 1) % numPages
}
}
struct PageIndicator: View {
@ObservedObject private var vm = PageIndicatorVM(numPages: 5)
private let spacing: CGFloat = 2
private let dotSize: CGFloat = 8
var body: some View {
VStack {
HStack(alignment: .center, spacing: spacing) {
ForEach((0..<vm.numPages ), id: \.self) {
DotIndicator(pageIndex: $0, isOn: self.$vm.currentPage)
.frame(
width: self.dotSize,
height: self.dotSize
)
}
}
}
}
}