SwiftUI:删除最后一个NavigationLink时崩溃

时间:2020-11-12 12:28:30

标签: macos swiftui swiftui-list swiftui-navigationlink ipados

在带有macOSiPadOS的SwiftUI上,当我使NavigationLink项的数量可变然后尝试删除最后一个项时,我崩溃了。删除除最后一项以外的其他所有项都很好。崩溃时,我得到了日志打印:

Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift, line 444

似乎destination中的NavigationLinkNavigationLink本身的生存时间更长,这在短时间内给destination带来了无效的绑定。仅当destination需要Binding(例如,能够编辑某些内容)时,才会显示崩溃。如果我将Text的视图用作destination而不是TextField,则不会崩溃。

这是一个复制的最小示例:

import SwiftUI

struct ContentView: View {
    @State var strings = ["Hello World 1", "Hello World 2", "Hello World 3"]
    @State var selectedStringIndex: Int?
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Remove Selected") { // when removing the last element => crash
                    if let selectedStringIndex = selectedStringIndex {
                        strings.remove(at: selectedStringIndex)
                    }
                }
                
                List(strings.indices, id: \.self, selection: $selectedStringIndex) { stringIndex in
                    NavigationLink(destination: TextField("Name", text: $strings[stringIndex]),
                                   tag: stringIndex, selection: $selectedStringIndex) {
                        Text(strings[stringIndex])
                    }
                }
            }
        }
    }
}

处理NavigationLink个项目的动态数量的推荐方法是什么?

我在Xcode 12.2 beta 4上使用macOS 11.0.1 Beta

1 个答案:

答案 0 :(得分:1)

一种可行的解决方案是使用自定义Binding并检查index是否在范围内

func binding(for index: Int) -> Binding<String> {
    .init(get: {
        guard strings.indices.contains(index) else { return "" } // check if `index` is valid
        return strings[index]
    }, set: {
        strings[index] = $0
    })
}

然后,您可以在NavigationLink中使用此绑定:

NavigationLink(destination: TextField("Name", text: binding(for: stringIndex)), ...)

还要确保删除后重置selectedStringIndex

Button("Remove Selected") {
    if let selectedStringIndex = selectedStringIndex {
        strings.remove(at: selectedStringIndex)
        self.selectedStringIndex = nil // reset here
    }
}