1. 9.地图标注实现

1.1.1. 十五、地图标注实现

第1步:新增标注协议、标注view协议

AnnotationProtocol(通用标注协议)、AnnotationViewProtocol(通用标注view协议)、PinAnnotationViewProtocol(大头针标注view协议)

/// 标注协议
public protocol AnnotationProtocol: NSObjectProtocol {

    /// 标注view中心坐标
    var coordinate: CLLocationCoordinate2D? { get set}

    /// annotation标题
    var title: String? { get set }

    /// annotation副标题
    var subtitle: String? { get set }

    /// 具体地图标注实现
    var annotationImpl: Any? { get }

    /// 构造方法
    /// - Parameter annotationImpl: 具体地图标注实现
    init(annotationImpl: Any?)
}
/// 标注view协议
public protocol AnnotationViewProtocol: NSObjectProtocol {

    /// 是否允许弹出callout
    var canShowCallout: Bool { get set }

    /// 显示在默认弹出框右侧的view
    var rightCalloutAccessoryView: UIView? { get set }

    /// 是否支持拖动
    var draggable: Bool { get set }

    /// 具体地图标注view实现
    var annotationViewImpl: UIView? { get }

    /// 构造方法
    /// - Parameter annotationViewImpl: 具体地图标注view实现
    init(annotationViewImpl: UIView?)

}
/// 大头针标注view颜色
public enum PinAnnotationColor: Int {
    ///< 红色大头针
    case red
    ///< 绿色大头针
    case green
    ///< 紫色大头针
    case purple
}

/// 大头针标注view协议
public protocol PinAnnotationViewProtocol: AnnotationViewProtocol {

    /// 大头针的颜色
    var pinColor: PinAnnotationColor { get set }

    /// 添加到地图时是否使用下落动画效果
    var animatesDrop: Bool { get set }
}

第2步:新增标注实现、标注view实现

GaodePointAnnotation(高德标注)、GaodePinAnnotationView(高德大头针)

BaiduPointAnnotation(百度标注)、BaiduPinAnnotationView(百度大头针)

PointAnnotation(通用标注)、PinAnnotationView(通用大头针)

/// 高德标注
public class GaodePointAnnotation: MAPointAnnotation {

    /// 聚合标注
    weak var annotation: PointAnnotation?
}
/// 高德大头针标准view
public class GaodePinAnnotationView: MAPinAnnotationView {

}
/// 百度标注
public class BaiduPointAnnotation: BMKPointAnnotation {

    /// 聚合标注
    weak var annotation: PointAnnotation?
}
/// 百度大头针标准view
public class BaiduPinAnnotationView: BMKPinAnnotationView {

}
/// 标注实现
public class PointAnnotation: NSObject, AnnotationProtocol {

    /// 标注view中心坐标
    public var coordinate: CLLocationCoordinate2D? {
        get {
            if let annotation = gaodeAnnotation {
                return annotation.coordinate
            }
            if let annotation = baiduAnnotation {
                return annotation.coordinate
            }
            return nil
        }
        set {
            if let coordinate = newValue, let annotation = gaodeAnnotation {
                annotation.coordinate = coordinate
            }
            if let coordinate = newValue, let annotation = baiduAnnotation {
                annotation.coordinate = coordinate
            }
        }
    }

    /// annotation标题
    public var title: String? {
        get {
            if let annotation = gaodeAnnotation {
                return annotation.title
            }
            if let annotation = baiduAnnotation {
                return annotation.title
            }
            return nil
        }
        set {
            if let title = newValue, let annotation = gaodeAnnotation {
                annotation.title = title
            }
            if let title = newValue, let annotation = baiduAnnotation {
                annotation.title = title
            }
        }
    }

    /// annotation副标题
    public var subtitle: String? {
        get {
            if let annotation = gaodeAnnotation {
                return annotation.subtitle
            }
            if let annotation = baiduAnnotation {
                return annotation.subtitle
            }
            return nil
        }
        set {
            if let subtitle = newValue, let annotation = gaodeAnnotation {
                annotation.subtitle = subtitle
            }
            if let subtitle = newValue, let annotation = baiduAnnotation {
                annotation.subtitle = subtitle
            }
        }
    }

    /// 具体地图标注实现
    private(set) public var annotationImpl: Any?

    /// 高德标注
    private var gaodeAnnotation: GaodePointAnnotation? {
        if let annotation = annotationImpl as? GaodePointAnnotation {
            return annotation
        }
        return nil
    }

    /// 百度标注
    private var baiduAnnotation: BaiduPointAnnotation? {
        if let annotation = annotationImpl as? BaiduPointAnnotation {
            return annotation
        }
        return nil
    }

    /// 构造方法
    /// - Parameter annotationImpl: 具体地图标注实现
    public required init(annotationImpl: Any?) {
        super.init()
        self.annotationImpl = annotationImpl
        gaodeAnnotation?.annotation = self
        baiduAnnotation?.annotation = self
    }
}
/// 通用大头针标注view
public class PinAnnotationView: NSObject, PinAnnotationViewProtocol {

    /// 是否允许弹出callout
    public var canShowCallout: Bool {
        get {
            if let annotationView = gaodeAnnotationView {
                return annotationView.canShowCallout
            }
            if let annotationView = baiduAnnotationView {
                return annotationView.canShowCallout
            }
            return false
        }
        set {
            if let annotationView = gaodeAnnotationView {
                annotationView.canShowCallout = newValue
            }
            if let annotationView = baiduAnnotationView {
                annotationView.canShowCallout = newValue
            }
        }
    }

    /// 显示在默认弹出框右侧的view
    public var rightCalloutAccessoryView: UIView? {
        get {
            if let annotationView = gaodeAnnotationView {
                return annotationView.rightCalloutAccessoryView
            }
            if let annotationView = baiduAnnotationView {
                return annotationView.rightCalloutAccessoryView
            }
            return nil
        }
        set {
            if let annotationView = gaodeAnnotationView {
                annotationView.rightCalloutAccessoryView = newValue
            }
            if let annotationView = baiduAnnotationView {
                annotationView.rightCalloutAccessoryView = newValue
            }
        }
    }

    /// 是否支持拖动
    public var draggable: Bool {
        get {
            if let annotationView = gaodeAnnotationView {
                return annotationView.isDraggable
            }
            if let annotationView = baiduAnnotationView {
                return annotationView.isDraggable
            }
            return false
        }
        set {
            if let annotationView = gaodeAnnotationView {
                annotationView.isDraggable = newValue
            }
            if let annotationView = baiduAnnotationView {
                annotationView.isDraggable = newValue
            }
        }
    }

    /// 大头针的颜色
    public var pinColor: PinAnnotationColor {
        get {
            if let annotationView = gaodeAnnotationView {
                return PinAnnotationColor(rawValue: annotationView.pinColor.rawValue) ?? .red
            }
            if let annotationView = baiduAnnotationView {
                return PinAnnotationColor(rawValue: Int(annotationView.pinColor)) ?? .red
            }
            return .red
        }
        set {
            if let annotationView = gaodeAnnotationView, let pinColor = MAPinAnnotationColor(rawValue: newValue.rawValue) {
                annotationView.pinColor = pinColor
            }
            if let annotationView = baiduAnnotationView {
                annotationView.pinColor = BMKPinAnnotationColor(newValue.rawValue)
            }
        }
    }

    /// 添加到地图时是否使用下落动画效果
    public var animatesDrop: Bool {
        get {
            if let annotationView = gaodeAnnotationView {
                return annotationView.animatesDrop
            }
            if let annotationView = baiduAnnotationView {
                return annotationView.animatesDrop
            }
            return false
        }
        set {
            if let annotationView = gaodeAnnotationView {
                annotationView.animatesDrop = newValue
            }
            if let annotationView = baiduAnnotationView {
                annotationView.animatesDrop = newValue
            }
        }
    }

    /// 具体地图标注view实现
    private(set) public var annotationViewImpl: UIView?

    /// 高德地图标注view
    private var gaodeAnnotationView: MAPinAnnotationView? {
        return annotationViewImpl as? MAPinAnnotationView
    }

    /// 百度地图标注view
    private var baiduAnnotationView: BMKPinAnnotationView? {
        return annotationViewImpl as? BMKPinAnnotationView
    }

    /// 构造方法
    /// - Parameter annotationViewImpl: 具体地图标注view实现
    public required init(annotationViewImpl: UIView?) {
        self.annotationViewImpl = annotationViewImpl
    }
}

第3步:地图工厂标准修改及实现

/// 地图工厂标准
public protocol MapFactoryProtocol: NSObjectProtocol {

    ......

    /// 获取地图标注
    func getPointAnnotation() -> PointAnnotation

    /// 获取标注view
    /// - Parameters:
    ///   - annotation: 标注
    ///   - reuseIdentifier: 重用ID
    func getPinAnnotationView(annotation: AnnotationProtocol, reuseIdentifier: String) -> PinAnnotationViewProtocol
}
/// 百度地图工厂
class BaiduMapFactory: NSObject, MapFactoryProtocol {
    ......

    /// 获取地图标注
    func getPointAnnotation() -> PointAnnotation {
        let annotationImpl = BaiduPointAnnotation()
        return PointAnnotation(annotationImpl: annotationImpl)
    }

    /// 获取标注view
    /// - Parameters:
    ///   - annotation: 标注
    ///   - reuseIdentifier: 重用ID
    func getPinAnnotationView(annotation: AnnotationProtocol, reuseIdentifier: String) -> PinAnnotationViewProtocol {

        var annotationImpl = BaiduPointAnnotation()
        if let annotationImplParam = annotation.annotationImpl as? BaiduPointAnnotation {
            annotationImpl = annotationImplParam
        }
        let annotationViewImpl = BaiduPinAnnotationView(annotation: annotationImpl, reuseIdentifier: reuseIdentifier)
        return PinAnnotationView(annotationViewImpl: annotationViewImpl)
    }
}
/// 高德地图工厂
class GaodeMapFactory: NSObject, MapFactoryProtocol {
    ......

    /// 获取地图标注
    func getPointAnnotation() -> PointAnnotation {
        let annotationImpl = GaodePointAnnotation()
        return PointAnnotation(annotationImpl: annotationImpl)
    }

    /// 获取标注view
    /// - Parameters:
    ///   - annotation: 标注
    ///   - reuseIdentifier: 重用ID
    func getPinAnnotationView(annotation: AnnotationProtocol, reuseIdentifier: String) -> PinAnnotationViewProtocol {
        var annotationImpl = GaodePointAnnotation()
        if let annotationImplParam = annotation.annotationImpl as? GaodePointAnnotation {
            annotationImpl = annotationImplParam
        }
        let annotationViewImpl = GaodePinAnnotationView(annotation: annotationImpl, reuseIdentifier: reuseIdentifier)
        return PinAnnotationView(annotationViewImpl: annotationViewImpl)
    }
}

第4步:地图协议修改及实现

/// 地图协议
public protocol MapViewProtocol: NSObjectProtocol {
    ......

    /// 向地图窗口添加标注
    /// - Parameter annotation: 要添加的标注
    func addAnnotation(_ annotation: AnnotationProtocol)

    /// 向地图窗口添加一组标注,需要实现MAMapViewDelegate的-mapView:viewForAnnotation:函数来生成标注对应的View
    /// - Parameter annotations: 要添加的标注数组
    func addAnnotations(_ annotations: [AnnotationProtocol])

    /// 设置地图使其可以显示数组中所有的annotation, 如果数组中只有一个则直接设置地图中心为annotation的位置。
    /// - Parameters:
    ///   - annotations: 需要显示的annotation
    ///   - animated: 是否执行动画
    func showAnnotations(_ annotations: [AnnotationProtocol], animated: Bool)

    /// 从复用内存池中获取制定复用标识的annotation view
    /// - Parameter withIdentifier: 复用标识
    /// - Returns: annotation view
    func dequeueReusableAnnotationView(withIdentifier: String) -> PinAnnotationViewProtocol?
}
/// 高德地图
class GaodeMapView: NSObject, MapViewProtocol {
    ......

    /// 向地图窗口添加标注
    /// - Parameter annotation: 要添加的标注
    func addAnnotation(_ annotation: AnnotationProtocol) {
        if let annotationImpl = annotation.annotationImpl as? MAAnnotation {
            mapView.addAnnotation(annotationImpl)
        }
    }

    /// 向地图窗口添加一组标注,需要实现MAMapViewDelegate的-mapView:viewForAnnotation:函数来生成标注对应的View
    /// - Parameter annotations: 要添加的标注数组
    func addAnnotations(_ annotations: [AnnotationProtocol]) {
        var annotationImpls: [Any] = []
        for annotation in annotations {
            if let annotationImpl = annotation.annotationImpl {
                annotationImpls.append(annotationImpl)
            }
        }
        mapView.addAnnotations(annotationImpls)
    }

    /// 设置地图使其可以显示数组中所有的annotation, 如果数组中只有一个则直接设置地图中心为annotation的位置。
    /// - Parameters:
    ///   - annotations: 需要显示的annotation
    ///   - animated: 是否执行动画
    func showAnnotations(_ annotations: [AnnotationProtocol], animated: Bool) {
        var annotationImpls: [Any] = []
        for annotation in annotations {
            if let annotationImpl = annotation.annotationImpl {
                annotationImpls.append(annotationImpl)
            }
        }
        mapView.showAnnotations(annotationImpls, animated: animated)
    }

    /// 从复用内存池中获取制定复用标识的annotation view
    /// - Parameter withIdentifier: 复用标识
    /// - Returns: annotation view
    func dequeueReusableAnnotationView(withIdentifier: String) -> PinAnnotationViewProtocol? {
        if let annotationViewImpl = mapView.dequeueReusableAnnotationView(withIdentifier: withIdentifier) as? GaodePinAnnotationView {
            return PinAnnotationView(annotationViewImpl: annotationViewImpl)
        }
        return nil
    }
}
/// 高德地图代理实现
class MAMapViewDelegateImpl: NSObject, MAMapViewDelegate {

    ......

    func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -> MAAnnotationView! {

        if let pointAnnotation = annotation as? GaodePointAnnotation, let anno = pointAnnotation.annotation {
            if let pinAnnotationView = delegate?.mapView(mapViewProtocol, viewFor: anno)?.annotationViewImpl as? MAAnnotationView {
                return pinAnnotationView
            }
            return nil
        }
        return nil
    }
}
/// 百度地图
class BaiduMapView: NSObject, MapViewProtocol {
    ......

    /// 向地图窗口添加标注
    /// - Parameter annotation: 要添加的标注
    func addAnnotation(_ annotation: AnnotationProtocol) {
        if let annotationImpl = annotation.annotationImpl as? BMKAnnotation {
            mapView.addAnnotation(annotationImpl)
        }
    }

    /// 向地图窗口添加一组标注,需要实现MAMapViewDelegate的-mapView:viewForAnnotation:函数来生成标注对应的View
    /// - Parameter annotations: 要添加的标注数组
    func addAnnotations(_ annotations: [AnnotationProtocol]) {
        var annotationImpls: [BMKAnnotation] = []
        for annotation in annotations {
            if let annotationImpl = annotation.annotationImpl as? BMKAnnotation {
                annotationImpls.append(annotationImpl)
            }
        }
        mapView.addAnnotations(annotationImpls)
    }

    /// 设置地图使其可以显示数组中所有的annotation, 如果数组中只有一个则直接设置地图中心为annotation的位置。
    /// - Parameters:
    ///   - annotations: 需要显示的annotation
    ///   - animated: 是否执行动画
    func showAnnotations(_ annotations: [AnnotationProtocol], animated: Bool) {
        var annotationImpls: [BMKAnnotation] = []
        for annotation in annotations {
            if let annotationImpl = annotation.annotationImpl as? BMKAnnotation {
                annotationImpls.append(annotationImpl)
            }
        }
        mapView.showAnnotations(annotationImpls, animated: animated)
    }

    /// 从复用内存池中获取制定复用标识的annotation view
    /// - Parameter withIdentifier: 复用标识
    /// - Returns: annotation view
    func dequeueReusableAnnotationView(withIdentifier: String) -> PinAnnotationViewProtocol? {
        if let annotationViewImpl = mapView.dequeueReusableAnnotationView(withIdentifier: withIdentifier) as? BaiduPinAnnotationView {
            return PinAnnotationView(annotationViewImpl: annotationViewImpl)
        }
        return nil
    }
}
/// 百度地图代理实现
class BMKMapViewDelegateImpl: NSObject, BMKMapViewDelegate {

    ......

    func mapView(_ mapView: BMKMapView!, viewFor annotation: BMKAnnotation!) -> BMKAnnotationView! {

        if let pointAnnotation = annotation as? BaiduPointAnnotation, let anno = pointAnnotation.annotation {
            if let pinAnnotationView = delegate?.mapView(mapViewProtocol, viewFor: anno)?.annotationViewImpl as? BMKAnnotationView{
                return pinAnnotationView
            }
        }
        return nil
    }

}

第5步:测试页面

class PointAnnotationViewController: UIViewController {

    /// 地图工厂
    private lazy var factory: MapFactoryProtocol = {
        let factory = MapEngine.shared.getFactory()!
        return factory
    }()

    /// 地图实例
    private lazy var mapView: MapViewProtocol = {
        // 显示地图
        let mapView = factory.getMapView(frame: view.bounds)
        // 设置代理
        mapView.delegate = self
        // 显示用户定位(放在设置代理之后,确保可以调用locationManager.requestAlwaysAuthorization())
        mapView.showsUserLocation = true
        // 跟踪模式
        mapView.userTrackingMode = .followWithHeading
        // 设置缩放
        mapView.zoomLevel = 16
        return mapView
    }()

    /// 标注数组
    private lazy var annotations: Array<PointAnnotation> = {
        var annotations = Array<PointAnnotation>()

        let coordinates: [CLLocationCoordinate2D] = [
            CLLocationCoordinate2D(latitude: 39.992520, longitude: 116.336170),
            CLLocationCoordinate2D(latitude: 39.978234, longitude: 116.352343),
            CLLocationCoordinate2D(latitude: 39.998293, longitude: 116.348904),
            CLLocationCoordinate2D(latitude: 40.004087, longitude: 116.353915),
            CLLocationCoordinate2D(latitude: 40.001442, longitude: 116.353915),
            CLLocationCoordinate2D(latitude: 39.989105, longitude: 116.360200),
            CLLocationCoordinate2D(latitude: 39.989098, longitude: 116.360201),
            CLLocationCoordinate2D(latitude: 39.998439, longitude: 116.324219),
            CLLocationCoordinate2D(latitude: 39.979590, longitude: 116.352792)]

        for (idx, coor) in coordinates.enumerated() {
            var anno = factory.getPointAnnotation()
            anno.coordinate = coor
            anno.title = String(idx)

            annotations.append(anno)
        }
        return annotations
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white
        view.addSubview(mapView.getView())

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        mapView.addAnnotations(annotations)
        mapView.showAnnotations(annotations, animated: false)
    }

}

extension PointAnnotationViewController: MapViewDelegateProtocol {

    func mapView(_ mapView: MapViewProtocol, viewFor annotation: AnnotationProtocol) -> PinAnnotationViewProtocol? {

        if annotation.isKind(of: PointAnnotation.self) {

            let pointReuseIndetifier = "pointReuseIndetifier"
            var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pointReuseIndetifier)
            if annotationView == nil {
                annotationView = factory.getPinAnnotationView(annotation: annotation, reuseIdentifier: pointReuseIndetifier)
            }
            annotationView!.canShowCallout = true
            annotationView!.animatesDrop = true
            annotationView!.draggable = true
            annotationView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

            let idx = annotations.index(of: annotation as! PointAnnotation)
            annotationView!.pinColor = PinAnnotationColor(rawValue: (idx ?? 3) % 3)!
            return annotationView
        }
        return nil
    }
}

results matching ""

    No results matching ""