问题描述
我有一个 SwiftUI 应用程序,我在其中显示了一张地图(使用由 UIViewRepresentable 制作的 Mapkit),其中显示了不同的注释。在每个注释上,我都实现了一个标注按钮,我想在按下按钮时完全改变主视图。
我的想法是使用主视图和地图视图的状态/绑定参数。这个想法可行,但我在使用地图视图的方式实现状态/绑定时遇到了一些困难。困难在于我将地图用作包含与地图交互的菜单的第二个视图的参数。我试图改变我使用地图视图的方式,但我没有找到任何解决方案...
我的想法是在 body 之前创建地图:@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,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
}
}
}
解决方法
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,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 ),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 == $0.id
})
if idx != nil && $0.first != nil{
results[idx!] = $0.first!
}
}))
}
}
}
struct MapViewParent_Previews: PreviewProvider {
static var previews: some View {
MapViewParent()
}
}
,
好吧,我尝试了很多方法,但对我来说没有任何效果,但是我找到了一个有效的解决方案,但是......它看起来不像是正确的方法......我没想到 TopMapMenu 会主动改变VStack 中的 Mapkit 但显然确实如此。如果它可以帮助某人或如果有人有更好的解释,我会发布新帖子。
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()
}
...