ios高德地图 custom annotaionView 用法

2018-01-22  本文已影响358人  姬歌

本文解决annotationView重叠时响应问题、未选中与选中状态下annotationView尺寸变化、标签变化问题。
按照官方给的api,实现简单的展示annotation本来是很简单的,但是要加一些自定义内容,就很变态了,毕竟只能继承他的AnnotationView,看不到实现源码,很多特性都要去摸索!

我们的需求是,地图上有一个人员集合,每个人有姓名-name,电话phone。要求默认状态是小头像+顶部name;点击后选中,变为大头像+顶部name&phone,选中后点击电话号码要弹出拨号AlertSheet。


图1、未选中 图2、选中

刚开始我是把label和icon通过代码生成一个图片,但是发现图片中name很模糊;找网上找了不少办法还是做不出高清文字。加上做成图片,没办法区分点击了icon还是name,所以放弃了这种办法。

最后参照官方Demo,使用CustomAnnotationView。(这不能用气泡callout,因为不选中的时候也要显示一个label)

Demo中是自定义calloutView,每次需要显示、隐藏就在setSelected内removeFromSuperView,addSubView去修改UI。我是在init方法里面全部初始化UI,然后在setSelected方法内,调用自定义方法annoLayoutSubViews去修改icon大小,label的大小、label的内容,最后修改整个CustomAnnotationView大小。可能是因为修改了CustomAnnotationView的frame,导致了每次点击后,地图上被点击的annoView就消失不见了,用手势缩放地图后,它才被显示出来。于是我想了一个办法,在setSelected里面添加了一个回调selectCallback。在回调里面缩放地图。

annotationView?.zIndex = theAno.zIndex
annotationView?.selectCallback = {
                annoisSelected, annoCoor in
                self.zoomCount += 1
                self.zoomQueue()
                if annoisSelected {
                    self.mapCenter = annoCoor
                }
            }
func setMapCenterForCoor(_ coor: CLLocationCoordinate2D) {
        zoomMap()
        self.aMapView.setCenter(coor, animated: true)
    }
    
    func zoomMap() {
        let zl = self.aMapView.zoomLevel
        let defaultFocusZoom = 15.0
        if zl < defaultFocusZoom {
            self.aMapView.zoomLevel = defaultFocusZoom + 0.1
        }else {
            self.aMapView.zoomLevel = defaultFocusZoom - 0.1
        }
    }
    //zoomQueue的目的是在短时间的,不管有多少个selectCallback,都只对地图做一次缩放
    func zoomQueue() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
            self.zoomCount -= 1
            if self.zoomCount == 0 {
                 self.setMapCenterForCoor(self.mapCenter)
            }else if self.zoomCount < 0 {
                self.zoomCount = 0
            }
        }
    }

开发中遇到另外一个问题是,每个annoView的所属区域为图2所示红色框范围。label(name+phone)之上,我添加了button,可以监测到点击事件,并进行block回调。但是点击已选中的annView的黄色区域,是没有任何响应的!点击红色区域,高德内部会执行- (void)setSelected:(BOOL)selected animated:(BOOL)animated方法。当有两个annoView重叠时,点击上方的annoView的黄色区域(透明的),下方的annoView是不会响应的!!!测试大哥就说:不行!于是我只能在annoView内重写了下面的方法:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let iconOrigin = self.iconView.frame.origin
        let iconSize = self.iconView.frame.size
        let suprInside = super.point(inside: point, with: event)
        if isSelected {
            if suprInside {
                if point.x >  iconOrigin.x &&
                    point.x < iconOrigin.x + iconSize.width &&
                    point.y > iconOrigin.y &&
                    point.y < iconOrigin.y + iconSize.height{
                    //点击了 icon图片
                }else if point.y > iconOrigin.y {
                    //点击了 icon 左右两侧(黄色区域)
                    DispatchQueue.main.async {
                        if Date().timeIntervalSince(self.lastPointTestDate) > 0.1 {
                            debugPrint("~~~~~~点击了 icon 左右两侧~~~~~~")
                            self.tapInside!(point)
                        }
                        self.lastPointTestDate = Date()
                    }
                }
            }
        }else {
//            debugPrint("~~~~~not selected~~~~~~~")
        }
        return suprInside
    }

再在回调方法中,重新判断到底应该选中哪一个annoView

annotationView?.tapInside = {
                point in
                let pointInMap = annotationView?.convert(point, to: self.aMapView)
                self.selectAnnoAtPoint(point: pointInMap!, exceptAnno: theAno)
            }
func selectAnnoAtPoint(point: CGPoint, exceptAnno: XXXAnotation) {
        
        let assumeUnselectedAnnoIconSize = CGSize(width: CGFloat(36.0), height: CGFloat(53.0))
        let assumeUnselectedAnnoIconY: CGFloat = 25.0
        
        for i in 0..<anotationArray.count {
            let index = anotationArray.count-i - 1
          //设置了annoView.zIndex,大值在上,默认为0。
          //倒叙取anno,从最上方的anno判断其对应的annoView是否处于被点击范围内
            let annotation = anotationArray[index]
            if annotation != exceptAnno {
                let annotationCenter = aMapView.convert(annotation.coordinate, toPointTo: aMapView)
                
                let annoIconX = annotationCenter.x - assumeUnselectedAnnoIconSize.width/2.0
                let annoIconY: CGFloat = annotationCenter.y - (assumeUnselectedAnnoIconSize.width + assumeUnselectedAnnoIconY)/2.0 + assumeUnselectedAnnoIconY
                
                if point.x > annoIconX &&
                    point.x < annoIconX + assumeUnselectedAnnoIconSize.width &&
                    point.y > annoIconY &&
                    point.y < annoIconY + assumeUnselectedAnnoIconSize.height {
                    //点击 范围在 此 anno 的 view内
                    self.selecetAnno(annotation)
                    break
                }
            }
        }
    }
func selecetAnno(_ anno: XXXAnotation) {
        
        if aMapView.selectedAnnotations.count == 1 {
            let selectedAnno = aMapView.selectedAnnotations[0]
            if selectedAnno is XXXAnotation {
                self.aMapView.deselectAnnotation(selectedAnno as! XXXAnotation, animated: true)
            }
        }
        //等待zoomQueue()结束后0.3s
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
            self.aMapView.selectAnnotation(anno, animated: false)
        }
    }

经过处理,即使点击一个已选中的annoView黄色区域,其下方的annoView仍然可以被直接点击

上一篇下一篇

猜你喜欢

热点阅读