Reputation: 816
I have a SwiftUI question which doesn't look like it's answered or a duplicate of another issue, and SwiftUI is rather new.
The following code, with my understanding, show toggle the background of the Button control. The number of likes increments as expected, but the state changes don't seem to pass through to the Book struct.
Everything I have read is that in the preview, you need to use:
.constant(x)
within the view of the PreviewProvider.
However, it doesn't work and the Button with likes does not the foreground, nor the background color.
When debugging the app, the result of the book isLiked does change. But the foreground/background colors do not.
Here is the code for the view.
import SwiftUI
struct BookDetailView: View {
@Binding var book: Book
@State var likes: Int = 0
var body: some View {
VStack {
Text(book.title)
Image(book.imageName)
.resizable()
.aspectRatio(contentMode: .fit)
.scaledToFit()
Button(action: {
self.book.isLiked.toggle()
self.likes = self.likes + 1
}) {
Text("👍 Like \(likes)")
.padding()
.foregroundColor(self.book.isLiked ? .secondary : .primary)
.background(self.book.isLiked ? Color.rayWenderlichGreen : Color.white)
.cornerRadius(10)
}
}
}
}
struct BookDetailView_Previews: PreviewProvider {
static var previews: some View {
BookDetailView(book: .constant(Book.demoBooks.randomElement()!))
}
}
Here is the actual book model
import SwiftUI
struct Book: Identifiable {
var id = UUID()
var title: String
var imageName: String
var isLiked = false
}
extension Book: Equatable {
static func == (lhs: Book, rhs: Book) -> Bool {
return lhs.id == rhs.id
}
}
I have invested some time in trying to understand why this is happening, and it seems like it could just be a bug with SwiftUI.
Any thoughts or suggestions?
Upvotes: 3
Views: 1755
Reputation: 257493
I cannot test because there is no Book type provided, but if it is a struct, the following should work (note: .constant
binding is constant, it does not modify wrapped value, you need alive one)
struct BookDetailView_Previews: PreviewProvider {
struct TestView: View {
@State private var book = Book.demoBooks.randomElement()!
var body: some View {
BookDetailView(book: $book)
}
}
static var previews: some View {
TestView()
}
}
Update: now with fix - the reason is that made equatable book does not take into account changed isLiked state, so view does not detect that anything changed and does not update.
The solution is either remove that extension or make it take into account all fields that should indicate modified value, as below
extension Book: Equatable {
static func == (lhs: Book, rhs: Book) -> Bool {
lhs.id == rhs.id && lhs.isLiked == rhs.isLiked
}
}
Tested & works with Xcode 11.4 / iOS 13.4
Upvotes: 3
Reputation: 743
Your code seems OK and your understanding, usage and implementation of the State concept appears to be good.
A weird thing is that this happens to me as well. Sometimes, State var just won't reload the view, and I believe it's because SwiftUI is unstable. I have a few ways to "fix" it.
@State private var toggleMe: Bool! = false
and call toggleMe.toggle()
to toggle it whenever I need a state change. This sometimes updates the UIobjectWillChange.send()
and it seems to work better then changing a State var
, for some reasonNone of these suggestions are good practice for your case. They are just a way of dealing with some stability issues SwiftUI has shown because it is new
Upvotes: 3