2025.01.15 工作变动原因,故将一些工作期间Tapd内部写的Wiki文档转移到个人博客。
新版本开发中,需要搜索多个行政区(市级),标注城市,绘制边界与大头针,记录一下开发相关的 重要代码(涉及高德地图SDK中的API部分),具体例子参考高德地图SDK的代码Demo
在这篇文档中,默认已导入高德地图SDK,如何导入和配置高德地图SDK不是这篇文章的重点,这里就不再赘述了。
一、MAMapView初始化 + 自定义地图
禁止大部份标注和手势,加载自定义地图。
阅读 高德地图SDK - 自定义地图 ,进入 控制台 - 我的地图样式 - 离线地图调用模式 - 发布 - 下载离线文件,把下载好的离线地图放入工程里。
// 地图
lazy var mapView: MAMapView = {
let mapView = MAMapView(frame: self.view.bounds)
mapView.delegate = self
mapView.showsUserLocation = false
mapView.showsCompass = false
mapView.isShowsLabels = false
mapView.setZoomLevel(4, animated: true)
mapView.isRotateEnabled = false
mapView.isRotateCameraEnabled = false
mapView.showsScale = false
// 开启自定义地图
let path = Bundle.main.path(forResource: "myMapStyle.data", ofType: "") ?? ""
let data: Data
do {
let data = try Data(NSData(contentsOfFile: path))
let options = MAMapCustomStyleOptions()
options.styleData = data
mapView.setCustomMapStyleOptions(options)
mapView.customMapStyleEnabled = true
} catch {
}
return mapView
}()
二、加载MAMapSearch,搜索行政区
创建AMapSearchAPI
lazy var mapSearch: AMapSearchAPI = {
let search = AMapSearchAPI()
search?.delegate = self
return search!
}()
发起行政区查询,因为要搜索多个城市,故做一遍遍历搜索。
// 清除所有旧数据(大头针 / 边界)
mapView.removeAnnotations(mapView.annotations)
mapView.removeOverlays(mapView.overlays)
for region in runningTrajectoryModel.regions {
// 行政区搜索
let dist = AMapDistrictSearchRequest()
// 搜索关键字
dist.keywords = region
// 返回边界
dist.requireExtension = true
// 查询的等级,0-返回当前行政区,1-返回所有下级行政区,2-返回所有下下级行政区
dist.subdistrict = 0
self.mapSearch.aMapDistrictSearch(dist)
}
三、行政区域查询代理回调处理
处理行政区搜索回调的数据,添加需要在地图上绘制的自定义对象。
注意这里要将 cityCount
替换成你将要搜索的城市的数据源数量!!!
// 处理搜索位置数据(大头针位置 & 行政区划线位置)
func onDistrictSearchDone(_ request: AMapDistrictSearchRequest!, response: AMapDistrictSearchResponse!) {
if response.count == 0 {
return
}
for aDistrict in response.districts {
// 查询本行政区
// 添加中心点大头针的数据对象
let coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(aDistrict.center.latitude), longitude: CLLocationDegrees(aDistrict.center.longitude))
let anno = MAPointAnnotation()
anno.coordinate = coordinate
anno.title = aDistrict.name
mapView.addAnnotation(anno)
// 添加绘制面的数据对象
if aDistrict.polylines != nil {
var polylines = Array<MAPolygon>()
for polylineString in aDistrict.polylines {
let polyline = polygonForCoordinateString(coordinateString: polylineString)
polylines.append(polyline!)
}
mapView.addOverlays(polylines)
}
}
// 搜到最后一个城市的时候,确定显示范围(本来用mapView.overlays的,但是overlays有时候一个城市不一定只有一个)
// cityCount = 数据源中需要搜索的城市数量
if let cityCount = 0, mapView.annotations.count == cityCount {
mapView.showOverlays(mapView.overlays, animated: true)
}
}
// 处理绘制面的边界数据对象
func polygonForCoordinateString(coordinateString: String) -> MAPolygon? {
if coordinateString.count == 0 { return nil }
var count: UInt = 0
let coordinates = coordinatesForString(coordinateString, coordinateCount: &count, token: ";")
let polyline = MAPolygon(coordinates: coordinates, count: count)
free(coordinates)
return polyline
}
// 处理定位边界的坐标点
func coordinatesForString(_ string: String, coordinateCount: inout UInt, token: String) -> UnsafeMutablePointer<CLLocationCoordinate2D>? {
var str = ""
if !(token == ",") {
str = string.replacingOccurrences(of: token, with: ",")
}
else {
str = string
}
let components = str.components(separatedBy: ",").map { Double($0) }
let count = components.count / 2
if count > 0 {
coordinateCount = UInt(count)
}
// 使用 UnsafeMutablePointer 分配内存
let coordinates = UnsafeMutablePointer<CLLocationCoordinate2D>.allocate(capacity: count)
for i in 0..<count {
if let longitude = components[2 * i], let latitude = components[2 * i + 1] {
coordinates[i].longitude = longitude
coordinates[i].latitude = latitude
} else {
// 如果转换失败,应处理错误或释放内存并返回 nil
coordinates.deallocate()
return nil
}
}
return coordinates
}
四、绘制自定义大头针 + 行政区边界
官方SDK中有提供默认的大头针样式,需求中需要 业务样式的大头针+城市名字,绘制自定义UIView中,需要注意规避View重用的问题。
在绘制城市标题的时候,引入了 SnapKit约束布局,在代码示例中添加了注释,以下是绘制代码
// 显示大头针
func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -> MAAnnotationView! {
if annotation.isKind(of: MAPointAnnotation.self) {
let pointReuseIndetifier = "pointReuseIndetifier"
var annotationView: MAPinAnnotationView? = mapView.dequeueReusableAnnotationView(withIdentifier: pointReuseIndetifier) as! MAPinAnnotationView?
if annotationView == nil {
annotationView = MAPinAnnotationView(annotation: annotation, reuseIdentifier: pointReuseIndetifier)
}
annotationView!.isDraggable = false
// 自定义大头针标注的图片
annotationView!.image = UIImage(named: "Annotation_myStyle")
// 如果已经有标题了,直接返回(解决重用)
for subView in annotationView!.subviews {
if let subLabel = subView as? UILabel {
// 城市标题
let regionString: String = annotation.title ?? ""
subLabel.text = regionString.replacingOccurrences(of: "市", with: "")
return annotationView!
}
}
// 城市标题
let regionString: String = annotation.title ?? ""
let titleLabel = UILabel()
titleLabel.text = regionString.replacingOccurrences(of: "市", with: "")
titleLabel.font = .systemFont(ofSize: 12, weight: .semibold)
titleLabel.textColor = .black
annotationView!.addSubview(titleLabel)
// 约束布局
titleLabel.snp.makeConstraints { make in
// 与大头针标注对齐
make.centerX.equalTo(annotationView!)
// 底部紧贴大头针顶部
make.bottom.equalTo(annotationView!.snp.top)
}
return annotationView!
}
return nil
}
绘制 行政区边界与填充颜色,可以直接使用官方的API MAPolygonRenderer
来进行绘制填充面,以下是示例代码:
// 显示边界
func mapView(_ mapView: MAMapView!, rendererFor overlay: MAOverlay!) -> MAOverlayRenderer! {
if overlay.isKind(of: MAPolygon.self) {
let renderer: MAPolygonRenderer = MAPolygonRenderer(overlay: overlay)
renderer.lineWidth = 1.0
renderer.strokeColor = .cyan
renderer.fillColor = .cyan.withAlphaComponent(0.5)
return renderer
}
return nil
}
五、最终完成效果
以下是实机演示的部分截图:
tapd_45228716_1732877912_140.png
这是 自定义离线地图(去掉所有道路河流),搜索行政区后最终的展示效果。
六、最后,完结撒花
告辞.jpeg