У меня есть простое приложение SwiftUI с боковой панелью и панелью подробностей, реализованное с помощью NavigationSplitView. Это работает нормально, за исключением того, что когда (программно) отменяется выбор элемента, выбранного в списке, приложение вылетает с бесполезной трассировкой стека (в основном ___lldb_unamed_symbol). В частности, когда selectedItem = nil происходит сбой приложения.
Проблему можно воспроизвести с помощью программы ниже:
import SwiftUI
struct Person: Hashable {
var firstName: String
var lastName: String
}
struct PersonDetailView: View {
var body: some View {
if let selectedPersonBinding = Binding($selection) {
VStack {
TextField("First Name", text: selectedPersonBinding.firstName)
TextField("Last Name", text: selectedPersonBinding.lastName)
}
.padding()
}
}
@Binding var selection: Person?
}
struct ContentView: View {
var body: some View {
NavigationSplitView {
List(people, id: \.self, selection: $selectedPerson) {
Text($0.firstName)
}
.toolbar {
Button("Deselect") {
selectedPerson = nil
}
}
} detail: {
PersonDetailView(selection: $selectedPerson)
}
.onAppear() {
selectedPerson = people[0]
}
}
let people = [
Person(firstName: "Steve", lastName: "Jobs"),
Person(firstName: "Steve", lastName: "Wozniak"),
Person(firstName: "Ronald", lastName: "Wayne")
]
@State var selectedPerson: Person?
}
Запустите это на Mac или iPad, нажмите кнопку «Отменить выбор», и приложение выйдет из строя.
Я пришел к выводу, что это как-то связано с привязкой подробного представления к selectedPerson, поскольку проблема исчезнет, если я удалю эту привязку, но я не могу понять, почему он должен аварийно завершить работу с этой привязкой.
Спасибо за комментарий. Это на самом деле не помогает, потому что не позволяет двустороннюю привязку к подсвойствам Person.





Похоже, это та же проблема, что и в этом посте, которая в основном рассматривается как ошибка SwiftUI. Это происходит всякий раз, когда вы используете инициализатор Binding для преобразования Binding<T?> в Binding<T>?, а затем передаете полученный Binding в какое-либо другое представление в операторе if. Минимальный воспроизводимый пример:
struct ContentView: View {
@State private var s: String? = "Foo"
var body: some View {
if let binding = Binding($s) {
TextField("Foo", text: binding)
}
Button("Foo") {
s = nil
}
}
}
Однако в вашем конкретном случае в вашем коде есть и другие ошибки, и если вы сделаете это правильно, вы избежите этой ошибки.
people, но people — это константа let. Вместо этого people должно быть @State var.$selectedPerson в подробное представление, поэтому будут меняться текстовые поля selectedPerson, а не люди в people. Вам следует передать привязку типа $people[selectedIndex].\.self в качестве идентификатора, поэтому при изменении любого из people идентификаторы строк списка изменяются, и все уничтожается и воссоздается. Это нежелательно. Person должен соответствовать Identifiable.Person в качестве типа выбора. Это означает, что каждое изменение выбранного человека меняет значение выбора. Опять же, это нежелательно. Тип выбора должен быть Person.ID.После этих изменений вы получите:
struct Person: Hashable, Identifiable {
var firstName: String
var lastName: String
let id = UUID()
}
struct PersonDetailView: View {
var body: some View {
VStack {
TextField("First Name", text: $selection.firstName)
TextField("Last Name", text: $selection.lastName)
}
.padding()
}
@Binding var selection: Person
}
struct ContentView: View {
var body: some View {
NavigationSplitView {
List(people, selection: $selectedPerson) {
Text($0.firstName)
}
.toolbar {
Button("Deselect") {
selectedPerson = nil
}
}
} detail: {
if let selectedPerson, let index = people.firstIndex(where: { $0.id == selectedPerson }) {
PersonDetailView(selection: $people[index])
}
}
}
@State var people = [
Person(firstName: "Steve", lastName: "Jobs"),
Person(firstName: "Steve", lastName: "Wozniak"),
Person(firstName: "Ronald", lastName: "Wayne")
]
@State var selectedPerson: UUID?
}
Спасибо! Какой чудесно ясно объясненный ответ. Я тоже не осознавал, что можно связать индекс с таким массивом.
Это ответ на ваш вопрос: stackoverflow.com/questions/57021722/swiftui-optional-textfield