如何在 SwiftUI 的新地图视图中制作可点击的图钉?

Posted

技术标签:

【中文标题】如何在 SwiftUI 的新地图视图中制作可点击的图钉?【英文标题】:How do I make a clickable pin in SwiftUI's new Map view? 【发布时间】:2020-08-01 23:49:06 【问题描述】:

我正在使用 SwiftUI 新地图视图来显示注释的图钉,并希望在单击图钉时显示用于编辑注释名称和描述的视图。

我尝试将 MapAnnotation 与包含带有图钉图像的按钮的视图一起使用。图像显示正确,但按钮不起作用。

是否可以在不回退到 MKMapView 的 UIViewRepresentable 的情况下做到这一点?

import SwiftUI
import MapKit

struct ContentView: View 
    @State private var showingEditScreen = false

    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 37.334722,
                                       longitude: -122.008889),
        span: MKCoordinateSpan(latitudeDelta: 1,
                               longitudeDelta: 1)
    )

    @State private var pins: [Pin] = [
        Pin(name: "Apple Park",
            description: "Apple Inc. headquarters",
            coordinate: CLLocationCoordinate2D(latitude: 37.334722,
                                               longitude:-122.008889))
    ]

    var body: some View 
        Map(coordinateRegion: $region,
            interactionModes: .all,
            annotationItems: pins,
            annotationContent:  pin in
                MapAnnotation(coordinate: pin.coordinate,
                              content: 
                                PinButtonView(pin: pin)
                              )
            )
    

我的 Pin 定义:

struct Pin: Identifiable 
    let id = UUID()
    var name: String
    var description: String
    var coordinate: CLLocationCoordinate2D


带有按钮和图钉图像的视图:

struct PinButtonView: View 
    @State private var showingEditScreen = false
    @State var pin: Pin

    var body: some View 
        Button(action: 
            showingEditScreen.toggle()
        ) 
            Image(systemName: "mappin")
                .padding()
                .foregroundColor(.red)
                .font(.title)
        
        .sheet(isPresented: $showingEditScreen,
               content: 
                EditView(pin: self.$pin)
               )
    

编辑视图:

struct EditView: View 
    @Environment(\.presentationMode) var presentationMode

    @Binding var pin: Pin

    var body: some View 
        NavigationView 
            Form 
                    TextField("Place name", text: $pin.name)

                    TextField("Description", text: $pin.description)
            
            .navigationTitle("Edit place")
            .navigationBarItems(trailing: Button("Done") 
                self.presentationMode.wrappedValue.dismiss()
            )
        
    

【问题讨论】:

我有一个带有地图视图和单个 MapAnnotation 的应用程序,我能够点击 MapAnnotation 来显示附加信息。此地图视图是导航堆栈上的推送视图,但是,如果我滚动地图视图,我将无法再点击 MapAnnotation,但如果我点击 MapAnnotation 所在的位置,MapAnnotation 仍会收到点击。在我的应用程序的另一部分,我有一个带有多个 MapAnnotation 的地图视图。我无法点击其中任何一个,地图视图都可以看到这些点击。此视图不在导航堆栈上。 谢谢,@JeffZacharias 我将 Map 放在 NavigationView 中,但仍然无法正常工作,甚至尝试将按钮更改为导航链接,但没有成功。您能否详细说明您是如何制作可点击的 MapAnnotation 的? 您是否将其放在 NavigationView 中,然后推送到具有地图和注释的第二个视图?如果它只是在 NavigationView 中,它对我不起作用,它必须在第二个推送视图中。 @JeffZacharias 尝试了你的建议,不出所料,滚动地图时得到了相同的行为。希望 Apple 将在未来的版本中修复这些故障。谢谢。 有人找到办法了吗?我正在努力解决,但到目前为止没有成功... 【参考方案1】:

从 XCode 12.3 开始,这似乎与按钮一起工作。我注意到的一个关键问题是,如果您使用偏移量,那么您的按钮可能会被放置在注释的可点击区域之外。

为了解决这个问题,您可以添加额外的填充来解释偏移量并将其保持在可点击的范围内:

MapAnnotation(coordinate: item.placemark.coordinate) 
  ZStack 
      MapPinView() // My view for showing a precise pin
      VStack  // A prompt with interactive option, offset by 60pt
          Text("Use this location?")
          HStack 
              Button("Yes", action: 
                  print("yes")
              )
              
              Button("No", action: 
                  print("no")
              )
          
      
      .offset(x: 0, y: 60) // offset prompt so it's below the marker pin
  
  .padding(.vertical, 60) // compensate for offset in tappable area of annotation. pad both the top AND the bottom to keep contents centered

【讨论】:

【参考方案2】:

我有一个 SwiftUI MapKit 视图,它使用 MapAnnotations 和一个可点击的图钉,它显示一个包含信息的视图。

它并不漂亮,但它仅适用于现在的 ios 14。

https://github.com/PhilStollery/BAB-Club-Search/blob/main/Shared/views/AnnotatedMapView.swift

import MapKit
import SwiftUI

struct AnnotatedMapView: View 
    
    @ObservedObject
    private var locationManager = LocationManager()
    
    // Default to center on the UK, zoom to show the whole island
    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 54.4609,
                                       longitude: -3.0886),
        span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10))
    
    @ObservedObject var store: ClubStore
    
    var body: some View 
        ZStack 
            Map(coordinateRegion: $region,
                showsUserLocation: true,
                annotationItems: store.clubs!) 
                    club in MapAnnotation(coordinate: club.coordinate) 
                        VStack
                            if club.show 
                                ZStack
                                    RoundedRectangle(cornerRadius: 10)
                                        .fill(Color(UIColor.systemBackground))
                                        .shadow(radius: 2, x: 2, y: 2)
                                    VStack
                                        NavigationLink(destination: ClubLocationView(club: club)) 
                                            Text(club.clubname)
                                                .fontWeight(.bold)
                                        
                                        .padding()
                                        Text(club.association)
                                            .padding(.leading)
                                            .padding(.trailing)
                                        Text(club.town)
                                            .padding(.bottom)
                                    
                                
                                .onTapGesture 
                                    let index: Int = store.clubs!.firstIndex(where: $0.id == club.id)!
                                    store.clubs![index].show = false
                                
                             else 
                                Image(systemName: "house.circle")
                                    .font(.title)
                                    .foregroundColor(.accentColor)
                                    .onTapGesture 
                                        let index: Int = store.clubs!.firstIndex(where: $0.id == club.id)!
                                        store.clubs![index].show = true
                                    
                            
                        
                    
            
            .edgesIgnoringSafeArea(.bottom)
        
        .navigationBarTitle(Text("Map View"), displayMode: .inline)
        .navigationBarItems(trailing:
                                Button(action: 
                                    withAnimation 
                                        self.region.center = locationManager.current!.coordinate
                                        self.region.span = MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
                                    
                                )
                                    Image(systemName: "location")
        )
    


【讨论】:

【参考方案3】:

使用 Xcode 12 GM 地图引脚仍然无法处理。

【讨论】:

我试过这种方式,但不知何故它似乎没有执行按钮操作。 @LucasSilveira 此时它正在工作,而今天没有!神秘的虫子。我在反馈协助中创建了错误。我鼓励你也这样做。 多么奇怪的行为。如果有什么变化并重新开始工作,请告诉我。谢谢 还尝试了一个新项目,最新的 XCode 12 Beta 6 — 未触发按钮操作。

以上是关于如何在 SwiftUI 的新地图视图中制作可点击的图钉?的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用 SwiftUI 制作可滑动的图像视图时,索引意外更改

我可以让 SCNBox 可点击和交互吗?

如何制作 SwiftUI 手势,在按下视图时继续运行代码

Android:超链接文本视图到地图位置?

如何使用 SwiftUI 制作具有绝对位置的底栏

如何在swiftUI中添加返回用户位置按钮?