I am very new to swift working on my first app and having trouble having a view update. I am passing an object into a new view, however the new view does not update when there is change in the Firebase Database. Is there a way to get updates on the Gridview? I though by passing the observed object from the StyleboardView it would update the GridView however Gridview does not update. I am having trouble finding a way for the new Gridview to update and reload the images.
struct StyleBoardView: View {
@State private var showingSheet = false
@ObservedObject var model = ApiModel()
@State var styleboardname = ""
let userEmail = Auth.auth().currentUser?.email
var body: some View {
NavigationView {
VStack {
Text("Select Style Board")
List (model.list) {item in
Button(item.styleboardname) {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
GridView(item: item)
}
}
struct GridView: View {
var item: Todo
@ObservedObject var model = ApiModel()
@State var newImage = ""
@State var loc = ""
@State var shouldShowImagePicker = false
@State var image: UIImage?
var body: some View {
NavigationView {
var posts = item.styleboardimages
VStack(alignment: .leading){
Text(item.styleboardname)
GeometryReader{ geo in
LazyVGrid(columns: [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
], spacing: 3 ){
ForEach(posts.sorted(by: <), id: \.key) { key, value in
if #available(iOS 15.0, *) {
AsyncImage(url: URL(string: value), transaction: Transaction(animation: .spring())) { phase in
switch phase {
case .empty:
Color.purple.opacity(0.1)
case .success(let image):
image
.resizable()
.scaledToFill()
case .failure(_):
Image(systemName: "exclamationmark.icloud")
.resizable()
.scaledToFit()
@unknown default:
Image(systemName: "exclamationmark.icloud")
}
}
.frame(width: 100, height: 100)
.cornerRadius(20)
CodePudding user response:
You have a few problems with the code. First of all, the original view that creates the view model, or has created for it originally, should own the object. Therefore you declare it as a @StateObject
.
struct StyleBoardView: View {
@State private var showingSheet = false
@StateObject var model = ApiModel() // @StateObject here
@State var styleboardname = ""
let userEmail = Auth.auth().currentUser?.email
var body: some View {
NavigationView {
VStack {
Text("Select Style Board")
List ($model.list) { $item in // Change this to pass a Binding
Button(item.styleboardname) {
showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
GridView(item: $item, model: model)
}
}
}
}
}
}
Since you are passing to a .sheet
, that will not automatically be re-rendered when StyleBoardView's
model changes, so you have to use a @Binding
to cause GridView
to re-render. Lastly, once you have your @StateObject
, you pass that to your next view. Otherwise, you continually make new models, so updates to one will not update the other.
struct GridView: View {
@Binding var item: Todo // Make this a @Binding so it reacts to the changes.
@ObservedObject var model: ApiModel // Pass the originally created view model in.
...
var body: some View {
NavigationView {
...
}
}
}
Lastly, you did not post a Minimal, Reproducible Example (MRE). You also did not post the complete GridView
struct. You may not even need your view model in that view as you do not use it in what you have posted.
CodePudding user response:
The problem is that you're initializing the model in an ObservedObject
, and passing it down to another initialized Observed Object.
What you actually wanna do is use an @StateObject
for where you initialize the model. And then use @ObservedObject
with the type of the model you're passing down so that:
struct StyleBoardView: View {
@StateObject var model = ApiModel()
/** Code **/
struct GridView: View {
@ObservedObject var model: ApiModel
Notice the difference, an @ObservedObject
should never initialize the model, it should only "inherit" (@ObservedObject var model: ApiModel
) a model from a parent View, in this case, ApiModel
.