SwiftUI - Mapkit - 绑定 mapkit 并在注释标注按钮上显示视图
SwiftUI - Mapkit - Binding mapkit and show view on annotation callout buttons
我有一个 SwiftUI 应用程序,我在其中显示了一张地图(使用由 UIViewRepresentable 制作的 Mapkit),其中显示了不同的注释。在每个注释上,我都实现了一个标注按钮,我想在按下按钮时完全更改主视图。
我的想法是使用主视图和 mapView 的 State/Binding 参数。这个想法可行,但我在使用地图视图的方式实现 State/Binding 时遇到了一些困难。困难在于我将地图用作第二个视图的参数,该视图包含与地图交互的菜单。我试图改变我使用地图视图的方式,但我没有找到任何解决方案...
我的想法是在正文之前创建地图:@State var map = MapView(displaySP: $displayStorePage)
但我做不到(错误:无法在 属性 初始值设定项中使用实例成员“$displayStorePage”;属性 'self' 可用之前的初始值设定项 运行)
非常欢迎任何帮助:)
编辑:附加代码显示,我尝试做最轻的版本。
所以在代码中,目标是从为注释激活的 Mapview 功能切换 Mainview 中的 displayStorePage
单击 func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
主视图:
import SwiftUI
import MapKit
struct MainView: View {
@State var researchStore: String = ""
@State var displayStorePage: Bool = false
@State var map = MapView()
var body: some View {
ZStack{
if displayStorePage == false {
VStack{
map.frame(height: 400)
}
TopMapMenu(map: $map)
}
if displayStorePage == true {
Text("This is a win !")
}
}
}
}
MapKit 视图:
import Combine
var storeLocation = [MKAnnotation]()
var newAnnotation = MKPointAnnotation()
let mapView = MKMapView()
var displayRedMarker: Bool = true
var displayStore: Bool = false
struct MapView: UIViewRepresentable {
class Coordinator: NSObject, MKMapViewDelegate{
var parent : MapView
init(_ parent: MapView){
self.parent = parent
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationView")
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "AnnotationView")
let annotationLabel = UILabel(frame: CGRect(x: -25, y: 12, width: 105, height: 30))
if displayRedMarker {
annotationLabel.text = annotation.title!!
} else {
annotationLabel.text = "Annotation to be hidden"
}
//Other markers done there ....
annotationView?.canShowCallout = true
let btn = UIButton(type: .detailDisclosure)
annotationView?.rightCalloutAccessoryView = btn
return annotationView
}
// Gestion click Annotation button
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
// Here : Changing the Main View parameter : displayStorePage to true
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> some UIView {
let region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 47.510053, longitude: 6.798374),
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.delegate = context.coordinator
mapView.showsUserLocation = true
mapView.setRegion(region, animated: false)
mapView.addAnnotation(AddAnnotations())
return mapView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
func AddAnnotations() -> MKAnnotation {
newAnnotation = MKPointAnnotation()
newAnnotation.title = "Title1"
newAnnotation.coordinate = CLLocationCoordinate2D(latitude: 47.510053, longitude: 6.798374)
return newAnnotation
}
}
TopMenuMap:
struct TopMapMenu: View {
@Binding var map : MapView
@State var searching: Bool = false
@State var redStore: Bool = true
@State var storeFound : Bool = false
var body: some View {
HStack{
VStack(alignment: .leading, spacing: 10){
Button(action: {
displayRedMarker.toggle()
// map.DeleteMarkers()
// map.AddMarkers()
redStore = displayRedMarker
}) {
HStack {
if redStore { Image(systemName: "heart.fill") }
else { Image(systemName: "heart") }
Text("Restaurant")
}
Spacer()
.background(Color(.white))
}
// Other Buttons(Pins) are display here
}// End VStack Pin stores
}
}
}
好吧,我尝试了很多东西,但对我来说没有任何效果,但我找到了一个可行的解决方案,但是......它看起来不像是正确的方法......我没想到 TopMapMenu 会积极更改 VStack 中的 Mapkit,但显然它确实如此。我 post 新 post 如果它可以帮助某人或者如果有人有更好的解释。
struct MainView: View {
@State var displayStorePage: Bool = false
@State var map = MapView(displaySP: Binding.constant(false))
var body: some View {
ZStack{
if displayStorePage == false {
VStack{
MapView(displaySP: $displayStorePage)
Spacer()
}
TopMapMenu(map: $map)
}
if displayStorePage == true {
StorePage()
}
}
}
对于 Mapkit:
struct MapView: UIViewRepresentable {
@Binding var displayStorePage: Bool
init(displaySP: Binding<Bool>) {
self._displayStorePage = displaySP
loadAnnotation()
}
...
import SwiftUI
import MapKit
class Store: Identifiable, ObservableObject {
let id: UUID = UUID()
@Published var title: String?
@Published var latitude: Double
@Published var longitude: Double
@Published var displayStoreImage: Bool
@Published var displayRedMarker: Bool
@Published var redStore: Bool
init(title: String, latitude: Double , longitude: Double, displayStoreImage: Bool, displayRedMarker: Bool, redStore: Bool ) {
self.title = title
self.latitude = latitude
self.longitude = longitude
self.displayRedMarker = displayRedMarker
self.displayStoreImage = displayStoreImage
self.redStore = redStore
}
}
extension Store{
var coordinate: CLLocationCoordinate2D{
get{
return CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude)
}
}
}
struct MapViewParent: View {
@State var selectedListing: Store?
@State var results: [Store] = [Store(title: "Point 1 title", latitude: 38.3 , longitude: 123.2, displayStoreImage: true, displayRedMarker: true, redStore: false ), Store(title: "Point 2 title", latitude: 38.4 , longitude: 123.1, displayStoreImage: true, displayRedMarker: false, redStore: true )
]
var body: some View {
VStack{
Button("clear-storepage", action: {
selectedListing = nil
})
if selectedListing != nil{
TopMapMenu(results: $results, selectedListing: $selectedListing)
}else{
MapViewAnnotations(selectedListing: $selectedListing, results: $results)
}
}
}
}
struct MapViewAnnotations: View {
@State var region: MKCoordinateRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 38.3 , longitude: 123.2 ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
@Binding var selectedListing: Store?
@Binding var results: [Store]
var body: some View {
Map(coordinateRegion: $region, annotationItems: results, annotationContent: {
listing in
MapAnnotation(coordinate: listing.coordinate, content: {
Button(action: {
self.selectedListing = listing as Store?
self.region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: listing.coordinate.latitude , longitude: listing.coordinate.longitude ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
}, label: {
if listing.displayStoreImage{
if listing.displayRedMarker{
Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
}else{
Image(systemName: "mappin").foregroundColor(.green).contentShape(Circle())
}
}
else{
Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
}
}).buttonStyle(PlainButtonStyle())
})
})
}
}
struct TopMapMenu: View {
@Binding var results: [Store]
@Binding var selectedListing: Store?
var body: some View {
List{
ForEach(results, id: \.id){store in
StoreView(store: store, selectedListing: $selectedListing, results: $results)
}
}
}
}
struct StoreView: View {
@ObservedObject var store: Store
@Binding var selectedListing: Store?
@Binding var results: [Store]
var body: some View{
HStack{
VStack(alignment: .leading, spacing: 10){
Button(action: {
store.displayRedMarker.toggle()
store.redStore = store.displayRedMarker
}, label: {
HStack {
if store.redStore {
Image(systemName: "heart.fill")
}
else { Image(systemName: "heart") }
Text("Restaurant")
}
Spacer()
.background(Color(.white))
}
)}
MapViewAnnotations(selectedListing: $selectedListing, results: Binding(get: {
return [store]
}, set: {
let idx = results.firstIndex(where: {
store.id == [=10=].id
})
if idx != nil && [=10=].first != nil{
results[idx!] = [=10=].first!
}
}))
}
}
}
struct MapViewParent_Previews: PreviewProvider {
static var previews: some View {
MapViewParent()
}
}
我有一个 SwiftUI 应用程序,我在其中显示了一张地图(使用由 UIViewRepresentable 制作的 Mapkit),其中显示了不同的注释。在每个注释上,我都实现了一个标注按钮,我想在按下按钮时完全更改主视图。
我的想法是使用主视图和 mapView 的 State/Binding 参数。这个想法可行,但我在使用地图视图的方式实现 State/Binding 时遇到了一些困难。困难在于我将地图用作第二个视图的参数,该视图包含与地图交互的菜单。我试图改变我使用地图视图的方式,但我没有找到任何解决方案...
我的想法是在正文之前创建地图:@State var map = MapView(displaySP: $displayStorePage)
但我做不到(错误:无法在 属性 初始值设定项中使用实例成员“$displayStorePage”;属性 'self' 可用之前的初始值设定项 运行)
非常欢迎任何帮助:)
编辑:附加代码显示,我尝试做最轻的版本。
所以在代码中,目标是从为注释激活的 Mapview 功能切换 Mainview 中的 displayStorePage
单击 func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
主视图:
import SwiftUI
import MapKit
struct MainView: View {
@State var researchStore: String = ""
@State var displayStorePage: Bool = false
@State var map = MapView()
var body: some View {
ZStack{
if displayStorePage == false {
VStack{
map.frame(height: 400)
}
TopMapMenu(map: $map)
}
if displayStorePage == true {
Text("This is a win !")
}
}
}
}
MapKit 视图:
import Combine
var storeLocation = [MKAnnotation]()
var newAnnotation = MKPointAnnotation()
let mapView = MKMapView()
var displayRedMarker: Bool = true
var displayStore: Bool = false
struct MapView: UIViewRepresentable {
class Coordinator: NSObject, MKMapViewDelegate{
var parent : MapView
init(_ parent: MapView){
self.parent = parent
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationView")
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "AnnotationView")
let annotationLabel = UILabel(frame: CGRect(x: -25, y: 12, width: 105, height: 30))
if displayRedMarker {
annotationLabel.text = annotation.title!!
} else {
annotationLabel.text = "Annotation to be hidden"
}
//Other markers done there ....
annotationView?.canShowCallout = true
let btn = UIButton(type: .detailDisclosure)
annotationView?.rightCalloutAccessoryView = btn
return annotationView
}
// Gestion click Annotation button
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
// Here : Changing the Main View parameter : displayStorePage to true
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> some UIView {
let region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 47.510053, longitude: 6.798374),
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
mapView.delegate = context.coordinator
mapView.showsUserLocation = true
mapView.setRegion(region, animated: false)
mapView.addAnnotation(AddAnnotations())
return mapView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
func AddAnnotations() -> MKAnnotation {
newAnnotation = MKPointAnnotation()
newAnnotation.title = "Title1"
newAnnotation.coordinate = CLLocationCoordinate2D(latitude: 47.510053, longitude: 6.798374)
return newAnnotation
}
}
TopMenuMap:
struct TopMapMenu: View {
@Binding var map : MapView
@State var searching: Bool = false
@State var redStore: Bool = true
@State var storeFound : Bool = false
var body: some View {
HStack{
VStack(alignment: .leading, spacing: 10){
Button(action: {
displayRedMarker.toggle()
// map.DeleteMarkers()
// map.AddMarkers()
redStore = displayRedMarker
}) {
HStack {
if redStore { Image(systemName: "heart.fill") }
else { Image(systemName: "heart") }
Text("Restaurant")
}
Spacer()
.background(Color(.white))
}
// Other Buttons(Pins) are display here
}// End VStack Pin stores
}
}
}
好吧,我尝试了很多东西,但对我来说没有任何效果,但我找到了一个可行的解决方案,但是......它看起来不像是正确的方法......我没想到 TopMapMenu 会积极更改 VStack 中的 Mapkit,但显然它确实如此。我 post 新 post 如果它可以帮助某人或者如果有人有更好的解释。
struct MainView: View {
@State var displayStorePage: Bool = false
@State var map = MapView(displaySP: Binding.constant(false))
var body: some View {
ZStack{
if displayStorePage == false {
VStack{
MapView(displaySP: $displayStorePage)
Spacer()
}
TopMapMenu(map: $map)
}
if displayStorePage == true {
StorePage()
}
}
}
对于 Mapkit:
struct MapView: UIViewRepresentable {
@Binding var displayStorePage: Bool
init(displaySP: Binding<Bool>) {
self._displayStorePage = displaySP
loadAnnotation()
}
...
import SwiftUI
import MapKit
class Store: Identifiable, ObservableObject {
let id: UUID = UUID()
@Published var title: String?
@Published var latitude: Double
@Published var longitude: Double
@Published var displayStoreImage: Bool
@Published var displayRedMarker: Bool
@Published var redStore: Bool
init(title: String, latitude: Double , longitude: Double, displayStoreImage: Bool, displayRedMarker: Bool, redStore: Bool ) {
self.title = title
self.latitude = latitude
self.longitude = longitude
self.displayRedMarker = displayRedMarker
self.displayStoreImage = displayStoreImage
self.redStore = redStore
}
}
extension Store{
var coordinate: CLLocationCoordinate2D{
get{
return CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude)
}
}
}
struct MapViewParent: View {
@State var selectedListing: Store?
@State var results: [Store] = [Store(title: "Point 1 title", latitude: 38.3 , longitude: 123.2, displayStoreImage: true, displayRedMarker: true, redStore: false ), Store(title: "Point 2 title", latitude: 38.4 , longitude: 123.1, displayStoreImage: true, displayRedMarker: false, redStore: true )
]
var body: some View {
VStack{
Button("clear-storepage", action: {
selectedListing = nil
})
if selectedListing != nil{
TopMapMenu(results: $results, selectedListing: $selectedListing)
}else{
MapViewAnnotations(selectedListing: $selectedListing, results: $results)
}
}
}
}
struct MapViewAnnotations: View {
@State var region: MKCoordinateRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 38.3 , longitude: 123.2 ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
@Binding var selectedListing: Store?
@Binding var results: [Store]
var body: some View {
Map(coordinateRegion: $region, annotationItems: results, annotationContent: {
listing in
MapAnnotation(coordinate: listing.coordinate, content: {
Button(action: {
self.selectedListing = listing as Store?
self.region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: listing.coordinate.latitude , longitude: listing.coordinate.longitude ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
}, label: {
if listing.displayStoreImage{
if listing.displayRedMarker{
Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
}else{
Image(systemName: "mappin").foregroundColor(.green).contentShape(Circle())
}
}
else{
Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
}
}).buttonStyle(PlainButtonStyle())
})
})
}
}
struct TopMapMenu: View {
@Binding var results: [Store]
@Binding var selectedListing: Store?
var body: some View {
List{
ForEach(results, id: \.id){store in
StoreView(store: store, selectedListing: $selectedListing, results: $results)
}
}
}
}
struct StoreView: View {
@ObservedObject var store: Store
@Binding var selectedListing: Store?
@Binding var results: [Store]
var body: some View{
HStack{
VStack(alignment: .leading, spacing: 10){
Button(action: {
store.displayRedMarker.toggle()
store.redStore = store.displayRedMarker
}, label: {
HStack {
if store.redStore {
Image(systemName: "heart.fill")
}
else { Image(systemName: "heart") }
Text("Restaurant")
}
Spacer()
.background(Color(.white))
}
)}
MapViewAnnotations(selectedListing: $selectedListing, results: Binding(get: {
return [store]
}, set: {
let idx = results.firstIndex(where: {
store.id == [=10=].id
})
if idx != nil && [=10=].first != nil{
results[idx!] = [=10=].first!
}
}))
}
}
}
struct MapViewParent_Previews: PreviewProvider {
static var previews: some View {
MapViewParent()
}
}