根据@Tushar Sharma 的建议,我从 PlaceDetail.swift 和 EditPlaceForm.swift 中的 @ObservedObject
变量中删除了 place
标记。这使得 place
属性成为视图的参数而不是状态对象。然后我在 EditPlaceForm.swift 中创建了单独的状态变量来保存文本输入的值。我在视图中添加了一个 onAppear,它使用提供的 place
对象设置状态值。这很好用,并解决了后退按钮消失的问题。不过,它引入了一个新问题:PlaceDetail
在关闭并重新打开之前不会更新。然后我做了一些测试,看看更新 place
内的 PlaceDetail
变量是否会导致更新。为此,我只是添加了一个按钮,该按钮更改了地点的名称,并在按下时将其保存到视图上下文中。视图在重新加载之前仍然没有更新。因此,我的问题已切换到如何在更新 place
时更新视图,而不是在 EditPlaceForm.swift 中使用 @ObservedObject
,这会导致后退按钮消失。
原文:
@ObservedObject var place: Place
更新:
var place: Place
更新:
//
// EditPlaceForm.swift
//
import SwiftUI
import CoreLocation
struct EditPlaceForm: View {
@Environment(\.presentationMode) private var presentationMode
@Environment(\.managedObjectContext) private var viewContext
var place: Place
@State private var name = ""
@State private var street = ""
@State private var city = ""
@State private var state = ""
@State private var zip = ""
@State private var country = ""
@State private var showAlert = false
@State private var alertText = ""
var body: some View {
NavigationView {
Form {
Section (header: Text("Name")) {
TextField("Name", text: $name)
}
Section (header: Text("Address")) {
VStack {
TextField("Street", text: $street)
TextField("City", text: $city)
HStack {
TextField("State", text: $state)
Divider()
TextField("ZIP", text: $zip)
}
TextField("Country", text: $country)
}
}
Section(header: Text("Save")) {
Button (action: {
save()
}, label: {
Text("Save")
})
}
}
.navigationTitle("Edit Place")
.alert(isPresented: $showAlert, content: {
Alert(title: Text("Error"), message: Text(alertText))
})
}
.onAppear {
loadPlaceData()
}
}
func loadPlaceData() {
name = place.name
street = place.street
city = place.city
state = place.state
zip = place.zip
country = place.country
}
func save() {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString("\(street), \(city) \(state), \(zip), \(country)", completionHandler: {
placemarks, error in
if error != nil {
print("Error forward geocoding")
print(error!.localizedDescription)
alertText = "Encountered geocoding error."
showAlert = true
return
}
if placemarks == nil {
print("No placemarks found")
alertText = "Location could not be found."
showAlert = true
return
}
let placemark = placemarks![0]
let testVars = [
placemark.thoroughfare,
placemark.locality,
placemark.administrativeArea,
placemark.country,
placemark.postalCode
]
for testVar in testVars {
if testVar == nil {
print("Not enough information to complete place")
alertText = "Not enough information."
showAlert = true
return
}
}
if placemark.location == nil {
print("Not enough information to complete place")
alertText = "Not enough information."
showAlert = true
return
}
viewContext.performAndWait {
place.street = placemark.subThoroughfare! + " " + placemark.thoroughfare!
place.city = placemark.locality!
place.state = placemark.administrativeArea!
place.country = placemark.country!
place.zip = placemark.postalCode!
place.latitude = placemark.location!.coordinate.latitude
place.longitude = placemark.location!.coordinate.latitude
do {
try viewContext.save()
presentationMode.wrappedValue.dismiss()
} catch {
print("ERROR: FAILED TO SAVE PLACE DETAILS")
alertText = "Failed to save."
showAlert = true
}
}
})
}
}
struct EditPlaceForm_Previews: PreviewProvider {
private static var viewContext = PersistenceController.preview.container.viewContext
static var previews: some View {
EditPlaceForm(place: Place.getDefault(context: viewContext))
}
}
我的应用有一个主页,它是一个导航视图。在该页面上,有导航链接。其中一个链接指向包含列表的“地点”页面。该页面上的列表项是名为 Place 的 CoreData 实体的详细信息页面的导航链接。在详细信息页面中,我有一个编辑按钮,可以将编辑表单作为工作表打开。
您可以使用导航链接从“地点”视图中打开详细信息视图。然后单击编辑按钮并更改一些信息。单击保存时,编辑表单将关闭,更改将反映在详细信息视图中。然后,您可以单击后退按钮返回“地点”视图。
您可以使用导航链接从“地点”视图中打开详细信息视图。然后单击编辑按钮并更改一些信息。保存时,编辑表单关闭,更改会反映在详细信息视图中,但返回按钮已消失,您卡在详细信息视图中。
视频:https://drive.google.com/file/d/1lzj-hOCb7hgMutQQwPl0ShR7l-c9sDib/view?usp=sharing
“地点”视图:
//
// PlacesList.swift
//
import SwiftUI
struct PlacesList: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Place.entity(), sortDescriptors: [
NSSortDescriptor(key: "name", ascending: true)
])
private var places: FetchedResults<Place>
@State private var showAddPlaceForm = false
var body: some View {
List {
ForEach (places) { place in
NavigationLink (destination: PlaceDetail(place: place)) {
PlaceRow(place: place)
}
}
.onDelete(perform: deleteRow)
}
.listStyle(PlainListStyle())
.navigationTitle(Text("Places"))
.navigationBarItems(trailing:
HStack {
Button(action: {
showAddPlaceForm = true
}, label: {
Image(systemName: "plus.circle")
.imageScale(.large)
})
.sheet(isPresented: $showAddPlaceForm) {
NewPlaceForm()
}
}
)
}
func deleteRow(at offsets: IndexSet) {
for offset in offsets {
viewContext.performAndWait {
viewContext.delete(places[offset])
try? viewContext.save()
}
}
}
}
struct PlacesList_Previews: PreviewProvider {
private static var viewContext = PersistenceController.preview.container.viewContext
static var previews: some View {
NavigationView {
PlacesList()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
}
PlaceRow.swift
//
// PlaceRow.swift
//
import SwiftUI
struct PlaceRow: View {
@Environment(\.managedObjectContext) private var viewContext
@ObservedObject var place: Place
var body: some View {
VStack (alignment: .leading) {
Text(place.name)
.font(.headline)
Group {
Text(place.street)
Text(place.city) + Text(", ") + Text(place.state)
}
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
struct PlaceRow_Previews: PreviewProvider {
private static var viewContext = PersistenceController.preview.container.viewContext
static var previews: some View {
List {
PlaceRow(place: Place.getDefault(context: viewContext))
}
}
}
PlaceDetail.swift
//
// PlaceDetail.swift
//
import SwiftUI
import CoreLocation
struct PlaceDetail: View {
@Environment(\.managedObjectContext) private var viewContext
@ObservedObject var place: Place
@State private var showEditForm = false
var body: some View {
List {
VStack (alignment: .leading) {
Text("Address")
.font(.headline)
Group {
Text(place.street)
Text(place.cityStateZIPString)
Text(place.country)
}
.font(.subheadline)
.foregroundColor(.secondary)
}
VStack {
MapView(place: place)
.frame(height: 300)
}
if place.encodedAddress != nil {
Link(destination:
URL(string: "http://maps.apple.com/?daddr=\(place.encodedAddress!)")!,
label: {
HStack {
Text("Get Directions with Apple Maps")
Spacer()
Image(systemName: "chevron.forward.circle")
.imageScale(.large)
}
.foregroundColor(.blue)
})
Link(destination:
URL(string: "https://www.google.com/maps/search/?api=1&destination=\(place.encodedAddress!)")!,
label: {
HStack {
Text("Get Directions with Google Maps")
Spacer()
Image(systemName: "chevron.forward.circle")
.imageScale(.large)
}
.foregroundColor(.blue)
})
}
}
.toolbar {
Button(action: {
showEditForm = true
}, label: {
Image(systemName: "pencil.circle")
})
}
.navigationTitle(place.name)
.sheet(isPresented: $showEditForm, content: {
EditPlaceForm(place: place)
})
}
}
struct PlaceDetail_Previews: PreviewProvider {
static var viewContext = PersistenceController.preview.container.viewContext
static var previews: some View {
NavigationView {
PlaceDetail(place: Place.getDefault(context: viewContext))
.environment(\.managedObjectContext, viewContext)
}
}
}
EditPlaceForm.swift
//
// EditPlaceForm.swift
//
import SwiftUI
import CoreLocation
struct EditPlaceForm: View {
@Environment(\.presentationMode) private var presentationMode
@Environment(\.managedObjectContext) private var viewContext
@ObservedObject var place: Place
@State private var showAlert = false
@State private var alertText = ""
var body: some View {
NavigationView {
Form {
Section (header: Text("Name")) {
TextField("Name", text: $place.name)
}
Section (header: Text("Address")) {
VStack {
TextField("Street", text: $place.street)
TextField("City", text: $place.city)
HStack {
TextField("State", text: $place.state)
TextField("ZIP", text: $place.zip)
}
TextField("Country", text: $place.country)
}
}
Section(header: Text("Save")) {
Button (action: {
save()
}, label: {
Text("Save")
})
}
}
.navigationTitle("Edit Place")
.alert(isPresented: $showAlert, content: {
Alert(title: Text("Error"), message: Text(alertText))
})
}
}
func save() {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(place.address, completionHandler: {
placemarks, error in
if error != nil {
print("Error forward geocoding")
print(error!.localizedDescription)
alertText = "Encountered geocoding error."
showAlert = true
return
}
if placemarks == nil {
print("No placemarks found")
alertText = "Location could not be found."
showAlert = true
return
}
let placemark = placemarks![0]
let testVars = [
placemark.thoroughfare,
placemark.locality,
placemark.administrativeArea,
placemark.country,
placemark.postalCode
]
for testVar in testVars {
if testVar == nil {
print("Not enough information to complete place")
alertText = "Not enough information."
showAlert = true
return
}
}
if placemark.location == nil {
print("Not enough information to complete place")
alertText = "Not enough information."
showAlert = true
return
}
viewContext.performAndWait {
place.street = placemark.subThoroughfare! + " " + placemark.thoroughfare!
place.city = placemark.locality!
place.state = placemark.administrativeArea!
place.country = placemark.country!
place.zip = placemark.postalCode!
place.latitude = placemark.location!.coordinate.latitude
place.longitude = placemark.location!.coordinate.latitude
do {
try viewContext.save()
presentationMode.wrappedValue.dismiss()
} catch {
print("ERROR: FAILED TO SAVE PLACE DETAILS")
}
}
})
}
}
struct EditPlaceForm_Previews: PreviewProvider {
private static var viewContext = PersistenceController.preview.container.viewContext
static var previews: some View {
EditPlaceForm(place: Place.getDefault(context: viewContext))
}
}