简体   繁体   中英

SwiftUI: Redraw after passing dynamic data to content view

I have something like below, except I have additional dynamic gestures within "ChildContentView."

How would I force a child view to redraw/reload? Currently, any dynamic content draws once and does not reload. All content is essentially static.

Update: Needed to use let content: () -> Content instead of @State var content: () -> Content .

// ChildContentView
struct ChildContentView<Content: View>: View {
    @State var content: () -> Content

    var body: some View {
        let dragGesture = DragGesture()...

        return Group {
            self.content()
        }.gesture(dragGesture)
    }
}
// ContentView
...
@State var changingList: [FooItem] = [] <-- This is dynamically changed.  Not reflected in this code snippet.

var body: some View {
    ZStack { 
        ChildContentView {
            List(changingList) { item in
                print("\(item)")
            }
        }
    }
}
...

Your trouble is the State property wrapper

struct ChildContentView<Content: View>: View {

@State var content: () -> Content

    var body: some View {
        let dragGesture = DragGesture()...

        return Group {
            self.content()
        }.gesture(dragGesture)
    }
}

It makes the content "static" (actually you did it :-))

Let see and try this example (copy - paste - run in your simulator)

import SwiftUI
struct Child<Content: View>: View {
    var content: () -> Content
    var body: some View {
        content()
    }
}

struct Item: Identifiable {
    let txt: String
    let id = UUID()
}

struct ContentView: View {
    @State var items = [Item(txt: "Alfa"), Item(txt: "Beta")]
    var body: some View {
        ZStack {
            Child {
                List(self.items) { (item) in
                    Text(item.txt)
                }
            }
            Button(action: {
                self.items.append(contentsOf: [Item(txt: "game"), Item(txt: "Delta")])
            }) {
                Text("Tap me to change items").padding().padding(.vertical, 50).background(Color.yellow)
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Ensure that an item in the list modifies it's id value when the content changes. Essentially hash the visual content to create a unique value per visual state.

eg

struct FooItem: Identifiable {
  var id: UUID = UUID()
  var text: String = ""

  var visualID: String {
    return id.uuidString
      + "\(text.hashValue)"
  }

}

Im guessing that the internal ZStack caching doesn't recognise that the object has changed when the id doesn't change.

So using a ForEach as that's what I've got in my code ( but List has id also )...

var body: some View {
    ZStack {
      ForEach(self.changingList, id: \.visualID) { item in
        SomeView(item: item)
          .onTapGesture {
            //blah blah
        }
      }
    }
}

I beat my head on this exact issue for days until working it out.

I am one month in to SwiftUI, so I don't fully understand why this works. However, setting the variable as a constant "let" passes the view content directly with updates.

Change from:

struct ChildContentView<Content: View>: View {
    @State var content: () -> Content

    var body: some View {
        let dragGesture = DragGesture()...

        return Group {
            self.content()
        }.gesture(dragGesture)
    }
}

to this:

struct ChildContentView<Content: View>: View {
    let content: () -> Content

    var body: some View {
        let dragGesture = DragGesture()...

        return Group {
            self.content()
        }.gesture(dragGesture)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM