swiftuiviewreusabilitycomputed-properties

SwiftUI: Computed Property from reusable View not working


Using the SwiftUI framework, I created this reusable View (minus styling bells and whistles):

struct RatingView: View {
    let criteria: String
    @State var rating: Int
    
    var body: some View {
        HStack {
            Button {
                if rating > 0 {
                    rating -= 1
                }
            } label: {
                Image(systemName: "heart.slash.fill")
            }
            
            Spacer()
            
            Text("\(criteria): \(rating)")

            Spacer()
            
            Button {
                if rating < 1 {
                    rating += 1
                }
            } label: {
                Image(systemName: "heart.fill")
            }
        }
    }
}

And implemented it here (again, without the styling bells and whistles):

struct ScoringView: View {
    let story = "Story"
    let cheese = "Cheese"
    let love = "Romance"
    let actor = "In character"
    
    var storyRating = 0
    var cheesyRating = 0
    var loveRating = 0
    var actorRating = 0
    
    var roundScore: Int {
        storyRating + cheesyRating + loveRating + actorRating
    }
    
    var body: some View {                 
        VStack {
            RatingView(criteria: story, rating: storyRating)
            RatingView(criteria: cheese, rating: cheesyRating)
            RatingView(criteria: love, rating: loveRating)
            RatingView(criteria: actor, rating: actorRating)
        }
                
        Spacer()
                
        VStack {
            Text("Score this round:")
            Text("\(roundScore)")
        }
    }
}

No matter the changes I make to the rating buttons, the roundScore computed property does not change and remains at zero.

I tested the computed property in a Playground, and it works! But since I wasn't able to find anything on SO about the combo of computed properties and reusable views, so I wonder if the reusable view is messing with me.

Any thoughts?


Solution

  • You need something yo trigger View updates or SwiftUI doesn't know when to redraw.

    Add @State

    @State var storyRating = 0
    @State var cheesyRating = 0
    @State var loveRating = 0
    @State var actorRating = 0
    

    and remove it from RatingView

    @Binding var rating: Int
    

    @State is a source of truth and @Binding is a two-way connection.

    You will also have to add $ when initializing views

    RatingView(criteria: story, rating: $storyRating)
    RatingView(criteria: cheese, rating: $cheesyRating)
    RatingView(criteria: love, rating: $loveRating)
    RatingView(criteria: actor, rating: $actorRating)