SwiftUI:更新变量时 NavigationLink 弹出
SwiftUI: NavigationLink pops back when variable is updated
我遇到了这个问题,当更新变量时,导航 link 弹出。这有点令人困惑,因为这种行为不会发生在应用程序的其他地方,但我正在做同样的事情。
首先,有一个符合 Identifiable 的 Vehicle 结构。在内容视图中,它显示在滚动视图的 NavigationView 中。
ScrollView(showsIndicators: false) {
VStack {
ForEach(user.vehicles) { vehicle in
VehicleListItem(user: $user,
vehicle: vehicle)
}
}
}
该列表项有一个导航 link,可将用户带到不同的视图。车辆作为绑定传递到下一个视图。
NavigationLink(destination: {
VehicleView(user: $user,
vehicle: $user.vehicles[user.vehicles.firstIndex(where: {[=11=].id == vehicle.id})!],
make: vehicle.make,
model: vehicle.model,
year: vehicle.year
)
}, label: {
Image(systemName: "car")
.font(.system(size: 100))
}).padding(.bottom, 20)
在车辆视图中,用户可以编辑车辆信息(制造商、型号、年份)并且工作正常。当用户对 Vehicle 进行更改时,它不会弹回到之前的视图。
Section() {
VehicleHandler(make: $make, model: $model, year: $year)
Button(action: {
//Update
vehicle.make = make
vehicle.model = model
vehicle.year = year
}, label: {
ButtonView(label: "Update")
}).disabled(make.isEmpty ||
model.isEmpty ||
(make == vehicle.make && model == vehicle.model && year == vehicle.year))
}
车辆处理程序
struct VehicleHandler: View {
enum Field: Hashable {
case make
case model
}
@FocusState private var field: Field?
@Binding var make: String
@Binding var model: String
@Binding var year: Int //Set to current year
var body: some View {
TextField("Make", text: $make)
.submitLabel(.next)
.focused($field, equals: .make)
.onSubmit {
field = .model
}
TextField("Model", text: $model)
.submitLabel(.done)
.focused($field, equals: .model)
Picker("Year", selection: $year, content: {
ForEach((1900...Date().year()).reversed(), id: \.self) { year in
Text(String(year))
.tag(year as Int)
}
})
}
}
现在,用户可以添加维护记录。一旦他们添加了一条记录,它就会使用 ForEach 显示。记录也符合可识别的。
//Only show 5
ForEach(vehicle.maintenanceRecords.prefix(5)) { record in
NavigationLink(destination: {
MaintenanceView(user: $user,
record: $vehicle.maintenanceRecords[vehicle.getMaintenanceIndex(id: record.id)],
date: record.date,
mileage: record.mileage ?? 0,
note: record.note,
cost: record.cost ?? 0,
tasks: record.tasks)
}, label: {
Text("\(record.date.formattedNoYear()) - \(record.note)")
})
}
相同的概念被用于编辑维护记录。记录作为绑定传递,传递记录信息以更新各个字段。当用户按下更新时,它会更新记录。正是在这一点上,只要您进行编辑并按更新,它就会弹出回 VehicleView。当我不使用 ID 时,我曾经遇到过这个问题,但是,每个结构都符合 Identifiable 并且 ID 没有被修改。我已经检查以确保它们保持不变,其中包括车辆 ID 和记录。有谁知道为什么它不断地回到这里,但在更新车辆信息时却没有?如果您需要更多信息,请告诉我。
RecordHandler(user: $user,
date: $date,
mileage: $mileage,
note: $note,
cost: $cost,
task: $task,
tasks: $tasks)
Section() {
Button(action: {
//Update record
record.date = date
record.note = note
record.tasks = tasks
if mileage.isZero == false {
record.mileage = mileage
}
if cost.isZero == false {
record.cost = cost
}
//Clear task
task = ""
}, label: {
ButtonView(label: "Update")
}).disabled(note.isEmpty ||
(date == record.date && mileage == record.mileage && note == record.note && cost == record.cost && tasks == record.tasks)
)
}
记录处理程序
struct RecordHandler: View {
enum Field: Hashable {
case note
case mileage
case cost
case task
}
@FocusState var field: Field?
@Binding var user: User
@Binding var date: Date
@Binding var mileage: Float
@Binding var note: String
@Binding var cost: Float
@Binding var task: String
@Binding var tasks: [Vehicle.Record.Task]
@State var suggestion: String = ""
var body: some View {
Section() {
DatePicker("Date", selection: $date)
TextField("Note", text: $note)
.submitLabel(.next)
.focused($field, equals: .note)
.onSubmit {
field = .mileage
}
FieldWithLabel(label: "Mileage", value: $mileage, formatter: NumberFormatter.number)
.submitLabel(.next)
.focused($field, equals: .mileage)
.onSubmit {
field = .cost
}
FieldWithLabel(label: "Cost", value: $cost, formatter: NumberFormatter.currency)
.submitLabel(.next)
.focused($field, equals: .cost)
.onSubmit {
field = .task
}
}
//For task
Section() {
HStack {
TextField("Task", text: $task)
.submitLabel(.done)
.focused($field, equals: .task)
.onSubmit {
addTask()
field = .task
}
.toolbar(content: {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Close") {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
})
.onChange(of: task, perform: { value in
//See if any of the saved task are similar
if user.tasks.contains(where: {[=16=].contains(value)}) {
//Set suggestion
suggestion = user.tasks.first(where: {[=16=].contains(value)})!
} else {
//Leave it empty if there's nothing
suggestion = ""
}
})
Spacer()
Button(action: {
addTask()
}, label: {
Image(systemName: "plus.circle.fill")
.font(.system(size: 22))
}).buttonStyle(BorderlessButtonStyle())
}
//To show task suggestions
if suggestion.isEmpty == false {
Button(action: {
//Set task to suggestion
task = suggestion
//Add task
addTask()
//Focus field
field = .task
}, label: {
HStack {
Text("Suggestion")
Spacer()
Text(suggestion)
}
})
}
ForEach(tasks) { task in
Text(task.name)
}
.onDelete(perform: delete)
}
}
func addTask() {
//Create
let task = Vehicle.Record.Task(name: task)
//Add to array
tasks.append(task)
//Clear
self.task = ""
}
func delete(at offsets: IndexSet) {
tasks.remove(atOffsets: offsets)
}
}
我遇到了同样的问题,今天成功解决了。尝试将选项 .navigationViewStyle(.stack)
添加到 NavigationView
struct ContentView: View {
var body: some View {
NavigationView {
//All your content and navigation links
}.navigationViewStyle(.stack)
}
}
我遇到了这个问题,当更新变量时,导航 link 弹出。这有点令人困惑,因为这种行为不会发生在应用程序的其他地方,但我正在做同样的事情。
首先,有一个符合 Identifiable 的 Vehicle 结构。在内容视图中,它显示在滚动视图的 NavigationView 中。
ScrollView(showsIndicators: false) {
VStack {
ForEach(user.vehicles) { vehicle in
VehicleListItem(user: $user,
vehicle: vehicle)
}
}
}
该列表项有一个导航 link,可将用户带到不同的视图。车辆作为绑定传递到下一个视图。
NavigationLink(destination: {
VehicleView(user: $user,
vehicle: $user.vehicles[user.vehicles.firstIndex(where: {[=11=].id == vehicle.id})!],
make: vehicle.make,
model: vehicle.model,
year: vehicle.year
)
}, label: {
Image(systemName: "car")
.font(.system(size: 100))
}).padding(.bottom, 20)
在车辆视图中,用户可以编辑车辆信息(制造商、型号、年份)并且工作正常。当用户对 Vehicle 进行更改时,它不会弹回到之前的视图。
Section() {
VehicleHandler(make: $make, model: $model, year: $year)
Button(action: {
//Update
vehicle.make = make
vehicle.model = model
vehicle.year = year
}, label: {
ButtonView(label: "Update")
}).disabled(make.isEmpty ||
model.isEmpty ||
(make == vehicle.make && model == vehicle.model && year == vehicle.year))
}
车辆处理程序
struct VehicleHandler: View {
enum Field: Hashable {
case make
case model
}
@FocusState private var field: Field?
@Binding var make: String
@Binding var model: String
@Binding var year: Int //Set to current year
var body: some View {
TextField("Make", text: $make)
.submitLabel(.next)
.focused($field, equals: .make)
.onSubmit {
field = .model
}
TextField("Model", text: $model)
.submitLabel(.done)
.focused($field, equals: .model)
Picker("Year", selection: $year, content: {
ForEach((1900...Date().year()).reversed(), id: \.self) { year in
Text(String(year))
.tag(year as Int)
}
})
}
}
现在,用户可以添加维护记录。一旦他们添加了一条记录,它就会使用 ForEach 显示。记录也符合可识别的。
//Only show 5
ForEach(vehicle.maintenanceRecords.prefix(5)) { record in
NavigationLink(destination: {
MaintenanceView(user: $user,
record: $vehicle.maintenanceRecords[vehicle.getMaintenanceIndex(id: record.id)],
date: record.date,
mileage: record.mileage ?? 0,
note: record.note,
cost: record.cost ?? 0,
tasks: record.tasks)
}, label: {
Text("\(record.date.formattedNoYear()) - \(record.note)")
})
}
相同的概念被用于编辑维护记录。记录作为绑定传递,传递记录信息以更新各个字段。当用户按下更新时,它会更新记录。正是在这一点上,只要您进行编辑并按更新,它就会弹出回 VehicleView。当我不使用 ID 时,我曾经遇到过这个问题,但是,每个结构都符合 Identifiable 并且 ID 没有被修改。我已经检查以确保它们保持不变,其中包括车辆 ID 和记录。有谁知道为什么它不断地回到这里,但在更新车辆信息时却没有?如果您需要更多信息,请告诉我。
RecordHandler(user: $user,
date: $date,
mileage: $mileage,
note: $note,
cost: $cost,
task: $task,
tasks: $tasks)
Section() {
Button(action: {
//Update record
record.date = date
record.note = note
record.tasks = tasks
if mileage.isZero == false {
record.mileage = mileage
}
if cost.isZero == false {
record.cost = cost
}
//Clear task
task = ""
}, label: {
ButtonView(label: "Update")
}).disabled(note.isEmpty ||
(date == record.date && mileage == record.mileage && note == record.note && cost == record.cost && tasks == record.tasks)
)
}
记录处理程序
struct RecordHandler: View {
enum Field: Hashable {
case note
case mileage
case cost
case task
}
@FocusState var field: Field?
@Binding var user: User
@Binding var date: Date
@Binding var mileage: Float
@Binding var note: String
@Binding var cost: Float
@Binding var task: String
@Binding var tasks: [Vehicle.Record.Task]
@State var suggestion: String = ""
var body: some View {
Section() {
DatePicker("Date", selection: $date)
TextField("Note", text: $note)
.submitLabel(.next)
.focused($field, equals: .note)
.onSubmit {
field = .mileage
}
FieldWithLabel(label: "Mileage", value: $mileage, formatter: NumberFormatter.number)
.submitLabel(.next)
.focused($field, equals: .mileage)
.onSubmit {
field = .cost
}
FieldWithLabel(label: "Cost", value: $cost, formatter: NumberFormatter.currency)
.submitLabel(.next)
.focused($field, equals: .cost)
.onSubmit {
field = .task
}
}
//For task
Section() {
HStack {
TextField("Task", text: $task)
.submitLabel(.done)
.focused($field, equals: .task)
.onSubmit {
addTask()
field = .task
}
.toolbar(content: {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("Close") {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
})
.onChange(of: task, perform: { value in
//See if any of the saved task are similar
if user.tasks.contains(where: {[=16=].contains(value)}) {
//Set suggestion
suggestion = user.tasks.first(where: {[=16=].contains(value)})!
} else {
//Leave it empty if there's nothing
suggestion = ""
}
})
Spacer()
Button(action: {
addTask()
}, label: {
Image(systemName: "plus.circle.fill")
.font(.system(size: 22))
}).buttonStyle(BorderlessButtonStyle())
}
//To show task suggestions
if suggestion.isEmpty == false {
Button(action: {
//Set task to suggestion
task = suggestion
//Add task
addTask()
//Focus field
field = .task
}, label: {
HStack {
Text("Suggestion")
Spacer()
Text(suggestion)
}
})
}
ForEach(tasks) { task in
Text(task.name)
}
.onDelete(perform: delete)
}
}
func addTask() {
//Create
let task = Vehicle.Record.Task(name: task)
//Add to array
tasks.append(task)
//Clear
self.task = ""
}
func delete(at offsets: IndexSet) {
tasks.remove(atOffsets: offsets)
}
}
我遇到了同样的问题,今天成功解决了。尝试将选项 .navigationViewStyle(.stack)
添加到 NavigationView
struct ContentView: View {
var body: some View {
NavigationView {
//All your content and navigation links
}.navigationViewStyle(.stack)
}
}