问题
I have an array of items (numbers) to be presented to the user using NavigationView, List and a leaf page.
When I update an item (numbers[index] = ...) on a leaf page, it updates the list correctly (which I see when I go back to the list), but not the leaf page itself immediately. I see the change if I go back to the list and re-open the same leaf page.
I would like to understand why it does not update the UI immediately, and how to fix it. Here is the simplified code to re-produce this behavior.
ADDITIONAL INFORMATION This code works fine on Xcode 12. It fails only on Xcode 12.1 (RC1) and Xcode 12.2 (beta3).
import SwiftUI
struct NumberHolder: Identifiable {
let id = UUID()
let value:Int
}
struct Playground: View {
@State var numbers:[NumberHolder] = [
NumberHolder(value:1),
NumberHolder(value:2),
NumberHolder(value:3),
NumberHolder(value:4),
]
var body: some View {
NavigationView {
List(numbers.indices) { index in
let number = numbers[index]
NavigationLink(destination: VStack {
Text("Number: \(number.value)")
Button("Increment") {
numbers[index] = NumberHolder(value: number.value + 1)
}
} ) {
Text("Number: \(number.value)")
}
}
}
}
}
struct Playground_Previews: PreviewProvider {
static var previews: some View {
Playground()
}
}
回答1:
This had me scratching my day for a few weeks.
It appears there are many features within SwiftUI that do not work in Views
that are placed in Lists
directly, however if you add a ForEach
inside the List
said features (such as .listRowPlatterColor(.green)
on WatchOS) start to work.
Solution
On iOS 14.2 if you wrap the NavigationLink
inside a ForEach
the NavigationLink
destination (leaf page) will update right away when the data model is updated.
So change your code to
var body: some View {
NavigationView {
List {
ForEach(numbers.indices) { index in
let number = numbers[index]
NavigationLink(destination: VStack {
Text("Number: \(number.value)")
Button("Increment") {
numbers[index] = NumberHolder(value: number.value + 1)
}
} ) {
Text("Number: \(number.value)")
}
}
}
}
}
Frustratingly, this does not solve the issue when using WatchOS, in WatchOS 7.0 the leaf page is updated, however in WatchOS 7.1 (version goes hand in hand with iOS 14.2 that suffered this "issue") so I have an issue open with Apple FB8892330
Further frustratingly, I still don't know if this is a bug or a feature in SwiftUI, none of the documentation state the requirement for ForEach
inside of Lists
回答2:
Try this one. I tested and it works.
import SwiftUI
struct NumberHolder: Identifiable {
let id = UUID()
let value:Int
}
struct ContentView: View {
@State var numbers:[NumberHolder] = [
NumberHolder(value:1),
NumberHolder(value:2),
NumberHolder(value:3),
NumberHolder(value:4),
]
var body: some View {
NavigationView {
List(numbers.indices) { index in
NavigationLink(destination: DetailView(numbers: $numbers, index: index)) {
Text("Number: \(self.numbers[index].value)")
}
}
}
}
}
struct DetailView: View {
@Binding var numbers:[NumberHolder]
let index: Int
var body: some View {
VStack {
Text("Number: \(self.numbers[index].value)")
Button("Increment") {
numbers[index] = NumberHolder(value: numbers[index].value + 1)
}
}
}
}
回答3:
I found the answer. It was a bug in Xcode. This code (without any changes) works fine under Xcode 12.0, but fails to update under Xcode 12.2 beta 2.
来源:https://stackoverflow.com/questions/64611300/swiftui-updating-an-array-item-does-not-update-the-child-ui-immediately