Reputation: 580
I'm trying to separate the logic from my UI in a simple quiz like example. But I am struggling with how to navigate away when I am finished with the questions.
In Xcode 12 I get error:
Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
I have seen from other answers that @State can only be used in struct, and suggest published. But I can't seem to figure out how to use a published property with a binding with it not "being installed on a View"
class ViewModel: ObservableObject {
@Published var currentQuestion: String!
private var questions = ["one", "two", "three"]
private var index = 0
init() {
currentQuestion = questions[index]
}
func button(title: String) -> some View {
Button(action: { [self] in
if index < questions.count {
index += 1
currentQuestion = questions[index]
} else {
print("go to results view")
}
}) {
Text(title)
}
}
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
NavigationView {
VStack {
viewModel.button(title: viewModel.currentQuestion)
}
}
}
}
struct ResultsView: View {
var body: some View {
Text("Results")
}
}
Any suggestions on what approach to take would be appreciated!
Upvotes: 6
Views: 11029
Reputation: 54436
You can try having all the logic in the ViewModel
:
class ViewModel: ObservableObject {
@Published var currentQuestion: String!
@Published var showResults = false
private var questions = ["one", "two", "three"]
private var index = 0
init() {
currentQuestion = questions[index]
}
func nextQuestion() {
if index < questions.count {
index += 1
currentQuestion = questions[index]
} else {
showResults = true
}
}
}
and UI components (eg. buttons) in the View:
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
NavigationView {
VStack {
Text(viewModel.currentQuestion)
Button(action: viewModel.nextQuestion) {
Text("Next")
}
}
// if viewModel.showResults == true -> navigate to results
.background(NavigationLink(destination: ResultsView(), isActive: $viewModel.showResults) { EmptyView() } .hidden())
}
}
}
In the above example you go to the ResultsView
using a NavigationLink
but you can easily replace the current view with results. The presentation is up to you.
Upvotes: 6