bpaul18
bpaul18

Reputation: 11

Trying to dynamically update swiftUI view

struct MakeVideo: View {
    
    @EnvironmentObject var modelData: ModelData

    @State private var chosenFriends: [FriendModel] = []
    
    mutating func addFriend(_friend: FriendModel) -> Void {
        chosenFriends.append(_friend)
        
    }
    
    var body: some View {
        VStack {
            ForEach(modelData.friends) { friend in
                HStack {
                    ProfilePic(picture: friend.profilepic!)
                    Text("@"+friend.username!)
                    
                    //TODO: This is updating the val, it just isn't being shown here
                    
                    Button("Add", action: friend.toggleChosen)
                    
                    if friend.isChosen {
                        Image(systemName: "star.fill")
                            .foregroundColor(.yellow)
                    } else {
                        Image(systemName: "star")
                    }
                    
                }
            }
        }
    }
}

struct MakeVideo_Previews: PreviewProvider {
    static var previews: some View {
        MakeVideo()
            .environmentObject(ModelData())
    }
}

I am trying to dynamically update this so that when ai click the Button, it'll make the star be filled instead of empty. In the debugger I see the class value being changed however the change does not appear in the view. I also made the var in the class @Published, which I thought would allow the view to change with the value

Here is my code for the classes and ModelData

class FriendModel: Identifiable, ObservableObject {
    
    init(id: Int, username: String, profilepic: String) {
        self.id = id
        self.username = username
        self.profilepic = profilepic
    }
    
    var id: Int?
    var username: String?
    var profilepic: String?
    @Published var isChosen = false
    //var profilepic: UIImage
    
    func toggleChosen() {
        print(self.isChosen)
        self.isChosen = !self.isChosen
        print(self.isChosen)
    }
}
var allvideos: [VideoModel] = [VideoModel(id: 1, name: "Beach Trip", length: 25, url: "mona"), VideoModel(id: 2, name: "Dogs", length: 10, url:"dog"), VideoModel(id: 3, name: "Concerts", length: 42, url: "hogrider")]

var allfriends: [FriendModel] = [FriendModel(id: 1, username: "bpaul18", profilepic: "profilepic"), FriendModel(id: 2, username: "kmill", profilepic: "profilepic"), FriendModel(id: 3, username: "dudeitsdom", profilepic: "profilepic")]

final class ModelData: ObservableObject {
    @Published var videos: [VideoModel] = allvideos
    @Published var friends: [FriendModel] = allfriends
}

Upvotes: 0

Views: 215

Answers (3)

malhal
malhal

Reputation: 30549

Body is only called for states that are used. Since chosenFriends is not used in body, it is not called when it is changed in a button action.

To fix, write a func isFriend and lookup the friend ID in the chosenFriends array and use the result of that in body to show the star. Since chosenFriends is now used, body will be called. Also change the button to call the addFriend func which would be better named as chooseFriend by the way.

Upvotes: 0

rob mayoff
rob mayoff

Reputation: 385500

You don't say that you get a compiler error on the following line:

Button("Add", action: friend.toggleChosen)

Therefore I deduce that FriendModel is a class, not a struct. If FriendModel were a struct and toggleChosen were a mutating method, then you would get an error: “Cannot use mutating member on immutable value”.

Even if FriendModel conforms to ObservableObject, the problem is that ObservableObjects do not automatically compose. A change to an @Published property of a FriendModel will not be noticed by a containing ModelData, which means (in this case) that SwiftUI will not notice that friend.isChosen was modified.

I suggest making FriendModel into a struct.

I also recommend using Point-Free's Composable Architecture package (or something similar) as your app architecture, because it provides a comprehensive solution to problems like this.

Upvotes: 1

try using this:

 ForEach($modelData.friends) { $friend in // <-- here $

Upvotes: 0

Related Questions