Map icon indicating copy to clipboard operation
Map copied to clipboard

Detail callout accessory view

Open pauljohanneskraft opened this issue 2 years ago • 6 comments
trafficstars

based on the work of @RomanPodymov

pauljohanneskraft avatar Apr 28 '23 15:04 pauljohanneskraft

I'm honestly thinking whether this is the best approach for this - maybe something like a view modifier on that annotation would be more suitable, but we would also deviate from the existing API there - but then again, afaik detailCalloutAccessory is not supported by the SwiftUI wrapper from MapKit anyways

pauljohanneskraft avatar Apr 28 '23 16:04 pauljohanneskraft

Hello @pauljohanneskraft Or you can keep a branch for "non-SwiftUI-API-features".

RomanPodymov avatar Apr 29 '23 04:04 RomanPodymov

@RomanPodymov What do you think about a solution like this:

#if !os(watchOS)

import Foundation
import MapKit
import SwiftUI

private struct DetailCalloutAccessory<Annotation: MapAnnotation, Content: View>: MapAnnotation {

    // MARK: Static Functions

    static func registerView(on mapView: MKMapView) {
        Annotation.registerView(on: mapView)
    }

    // MARK: Stored Properties

    private let wrappedAnnotation: Annotation
    private let content: () -> Content

    // MARK: Computed Properties

    var annotation: MKAnnotation {
        wrappedAnnotation.annotation
    }

    // MARK: Initialization

    init(_ annotation: Annotation, @ViewBuilder content: @escaping () -> Content) {
        self.wrappedAnnotation = annotation
        self.content = content
    }

    // MARK: Methods

    func view(for mapView: MKMapView) -> MKAnnotationView? {
        guard let view = wrappedAnnotation.view(for: mapView) else {
            return nil
        }

        if Content.self != EmptyView.self {
            view.canShowCallout = true
            view.detailCalloutAccessoryView = NativeHostingController(rootView: content()).view
        } else {
            view.canShowCallout = false
            view.detailCalloutAccessoryView = nil
        }

        return view
    }

}

extension MapAnnotation {

    public func detailCalloutAccessory<Content: View>(@ViewBuilder content: @escaping () -> Content) -> some MapAnnotation {
        DetailCalloutAccessory(self, content: content)
    }

}

#endif

It could then be used on any MapAnnotation using the following construct:

ViewMapAnnotation(...) {
    AnnotationContent()
}
.detailCalloutAccessory {
    DetailCalloutAccessoryContent()
}

pauljohanneskraft avatar Apr 30 '23 12:04 pauljohanneskraft

Hello @pauljohanneskraft Looks great. I don't have that much experience with SwiftUI, so I trust you.

RomanPodymov avatar Apr 30 '23 14:04 RomanPodymov

For what it's worth, it doesn't look like iOS 17 adds callouts to the SwiftUI Map component based on the initial beta documentation at https://developer.apple.com/documentation/mapkit/mapkit_for_swiftui

The proposed solution at https://github.com/pauljohanneskraft/Map/pull/46#issuecomment-1529020296 looks reasonable to me.

darronschall avatar Jun 08 '23 01:06 darronschall

Hello @darronschall If it is similar to iOS 17 SwiftUI Map API we can try this way.

RomanPodymov avatar Jun 08 '23 05:06 RomanPodymov