Reputation: 2525
I am trying to display a location in Map, allow user to search a location and update the location, then save the location.
When retrieving the data from model.object.location is is not displaying in Map, the data is saved.
LocationObject
struct Location: Identifiable, Codable {
static func == (lhs: Location, rhs: Location) -> Bool {
return lhs.id == rhs.id
}
var id = UUID().uuidString
var geopoint = GeoPoint(latitude: 51.510357, longitude: -0.116773)
var postcode: String = ""
var street: String = ""
var city: String = ""
var region: String = ""
var country: String = ""
var title: String? = ""
var subtitle: String? = ""
var longitude: Double? = -0.116773
var latitude: Double? = 51.510357
var coordinate: CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: latitude ?? 51.510357, longitude: longitude ?? -0.116773)
}
}
This is used with a ViewModel
, for example model.object.location
MapView
Show the map with data from Location object, and update view from SearchView.
@EnvironmentObject var model: Model
@State var selectedLocation: LocalSearchViewData? = nil
@State var position = MapCameraPosition.region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 51.510357, longitude: -0.116773),
latitudinalMeters: 500, longitudinalMeters: 500)
)
Map(position: $position) {
if selectedLocation != nil {
Marker("\(selectedLocation!.street)", coordinate: selectedLocation!.coordinate)
.tint(Color("Accent"))
}
}
.sheet(isPresented: $showDetail) {
SearchLocationView(selectedLocation: $selectedLocation, position: $position)
}
.onAppear {
// Location
if model.object!.location != nil {
print(LOCATION")
print(model.object.location!) <--- THIS PRINTS THE LOCATION CORRECT, SHOWING DATA DOES SAVE
selectedLocation?.latitude = (model.object!.location!.latitude)!
selectedLocation?.latitude = (model.object!.location!.longitude)!
if let long = model.object?.location?.longitude {
if let lat = model.object?.location?.latitude {
print(long) <--- This logs correct
position = MapCameraPosition.region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: lat, longitude: long),
latitudinalMeters: 500, longitudinalMeters: 500)
)
}
}
}
}
SearchView
struct SearchLocationView: View {
@Environment(\.presentationMode) var presentationMode
@StateObject private var viewModel = LocationModel()
@Binding var selectedLocation: LocalSearchViewData?
@Binding var position: MapCameraPosition
var body: some View {
GeometryReader { geometryProxy in
NavigationStack() {
ZStack(alignment: .bottom) {
ScrollView() {
VStack(alignment: .leading) {
HStack(alignment: .center, spacing: 16) {
TextField("Enter Address", text: $viewModel.cityText)
.modifier(SearchTextFieldStyle())
}
List(viewModel.viewData) { item in
VStack(alignment: .leading) {
Text(item.title)
Text(item.subtitle).foregroundColor(.secondary)
}
.hoverEffect()
.onTapGesture {
self.selectedLocation = item
position = MapCameraPosition.region(
MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: selectedLocation?.latitude ?? 5555.5, longitude: selectedLocation?.longitude ?? 5555.5 ),
latitudinalMeters: 500, longitudinalMeters: 500))
self.presentationMode.wrappedValue.dismiss()
}
}
}
.padding(.horizontal)
.padding(.top, 60)
.frame(
width: geometryProxy.size.width,
height: geometryProxy.size.height,
alignment: .center
)
}
}
.frame(
width: geometryProxy.size.width,
height: geometryProxy.size.height,
alignment: .center
)
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button {
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "arrow.left")
.font(.system(size: 20, weight: .semibold))
}
.buttonStyle(TopNavButton())
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
viewModel.cityText = ""
viewModel.viewData.removeAll()
} label: {
Text("Clear")
}
.buttonStyle(.plain)
.hoverEffect()
}
}
}
}
#if os(visionOS)
.frame(minWidth: 520)
.frame(minHeight: 680)
#endif
}
}
struct LocalSearchViewData: Identifiable {
var id = UUID()
var title: String
var subtitle: String
var street: String
var city: String
var country: String
var postcode: String
var longitude: Double = 0.0
var latitude: Double = 0.0
var coordinate: CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
init(mapItem: MKMapItem) {
self.title = mapItem.name ?? ""
self.subtitle = mapItem.placemark.title ?? ""
if let s = mapItem.placemark.postalAddress?.street {
self.street = s
} else {
self.street = ""
}
if let s = mapItem.placemark.postalAddress?.city {
self.city = s
} else {
self.city = ""
}
if let s = mapItem.placemark.postalAddress?.country {
self.country = s
} else {
self.country = ""
}
if let s = mapItem.placemark.postalAddress?.postalCode {
self.postcode = s
} else {
self.postcode = ""
}
longitude = mapItem.placemark.coordinate.longitude
latitude = mapItem.placemark.coordinate.latitude
}
}
final class LocalSearchService {
let localSearchPublisher = PassthroughSubject<[MKMapItem], Never>()
private let center: CLLocationCoordinate2D
private let radius: CLLocationDistance
init(in center: CLLocationCoordinate2D,
radius: CLLocationDistance = 350_000) {
self.center = center
self.radius = radius
}
public func searchCities(searchText: String) {
request(resultType: .address, searchText: searchText)
}
public func searchPointOfInterests(searchText: String) {
request(searchText: searchText)
}
private func request(resultType: MKLocalSearch.ResultType = .pointOfInterest,
searchText: String) {
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = searchText
request.pointOfInterestFilter = .includingAll
request.resultTypes = resultType
request.region = MKCoordinateRegion(center: center,
latitudinalMeters: radius,
longitudinalMeters: radius)
let search = MKLocalSearch(request: request)
search.start { [weak self](response, _) in
guard let response = response else {
return
}
self?.localSearchPublisher.send(response.mapItems)
}
}
}
final class LocationModel: ObservableObject {
private var cancellable: AnyCancellable?
@Published var cityText = "" {
didSet {
searchForCity(text: cityText)
}
}
@Published var poiText = "" {
didSet {
searchForPOI(text: poiText)
}
}
@Published var viewData = [LocalSearchViewData]()
var service: LocalSearchService
init() {
// New York
let center = CLLocationCoordinate2D(latitude: 40.730610, longitude: -73.935242)
service = LocalSearchService(in: center)
cancellable = service.localSearchPublisher.sink { mapItems in
self.viewData = mapItems.map({ LocalSearchViewData(mapItem: $0) })
}
}
private func searchForCity(text: String) {
service.searchCities(searchText: text)
}
private func searchForPOI(text: String) {
service.searchPointOfInterests(searchText: text)
}
}
Save
func saveLocation() {
if selectedLocation != nil {
model.object?.location?.street = selectedLocation!.street
model.object?.location?.city = selectedLocation!.city
model.object?.location?.country = selectedLocation!.country
model.object?.location?.postcode = selectedLocation!.postcode
model.object?.location?.title = selectedLocation!.title
model.object?.location?.subtitle = selectedLocation!.subtitle
model.object?.location?.latitude = selectedLocation!.latitude
model.object?.location?.longitude = selectedLocation!.longitude
}
}
Upvotes: 0
Views: 111