• iOS 地图定位及大头针的基本使用


    地图 Part1 - 定位及大头针的基本使用

    一.MapKit

    • 作用 : 用于地图展示

    • 如大头针,路线,覆盖层展示等(着重界面展示)

    • 使用步骤

      • 导入头文件
      #import <MapKit/MapKit.h>
      
    • MapKit有一个比较重要的UI控件

      • MKMapView, 专门用来地图显示

    二.地图的基本使用

    0.首先在storyboard上添加一个地图控件 - MapKitView

    • 连线控制器
    @IBOutlet weak var mapView: MKMapView!
    

    1.设置地图的类型

    • 方法
    // 可根据地图类型自己设定
    mapView.mapType = .standard
    
    • 地图的类型
    @available(iOS 3.0, *)
    public enum MKMapType : UInt {
    
        case standard			// 普通地图 (默认)
        case satellite			// 卫星云图
        case hybrid				// 混合地图(卫星云图+普通地图)
        
        @available(iOS 9.0, *)
        case satelliteFlyover	// 3D卫星地图
    
        @available(iOS 9.0, *)
        case hybridFlyover		// 3D混合卫星地图(3D卫星地图+普通地图)
    }
    

    2.设置地图的操作项

    • false就是取消这些功能
    // 缩放
    mapView.isZoomEnabled = false
    // 旋转
    mapView.isRotateEnabled = false
    // 滚动
    mapView.isScrollEnabled = false
    

    3.设置地图的显示项

    // 设置地图显示项(3D卫星混合信息)
    if #available(iOS 9.0, *) {
        mapView.showsCompass = true     // 指南针
     	mapView.showsTraffic = true     // 交通
        mapView.showsScale = true       // 比例尺
    	}
    // 设置地图显示项
    mapView.showsBuildings = true   // 建筑物
    mapView.showsPointsOfInterest = true    // 兴趣点
    

    4.在iOS 8.0之后定位需要主动授权

    • 懒加载位置管理者,请求授权写在里面
    lazy var locationM : CLLocationManager = {
    	let locationM = CLLocationManager()
    	if #available(iOS 8.0, *) {
    	// 前后台授权
    	locationM.requestAlwaysAuthorization()
            }
    	return locationM
    }()
    
    • 外界调用locationM的get方法,执行授权
    • 定位,但不会追踪
    _ = locationM
    

    5.设置用户的追踪模式

    • 有一个缺陷
      • 只要动一下地图,就不再追踪用户的位置(不是很灵敏)
    // 带方向的追踪
    mapView.userTrackingMode = .followWithHeading
    
    • 其他追踪模式
    @available(iOS 5.0, *)
    public enum MKUserTrackingMode : Int {
    
        case none // 不追踪,也不会显示用户的位置(相当于showsUserLocation为false)
    
        case follow // 追踪,会显示用户的位置showsUserLocation为true
    
        case followWithHeading // 带方向的追踪,showsUserLocation为true
     
    }
    
    

    6.代理方法

    • mapView设置代理
    mapView.delegate = self
    
    • 代理方法
    6.1 当用户位置改变时
    /// 当用户位置改变时就会来到这个方法
    /// 在地图上显示一个蓝色的圆点来标注用户的位置
    ///
    /// - Parameters:
    ///   - mapView: 地图视图
    ///   - userLocation: 大头针数据模型
    func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
            
    // print("用户位置改变")
    // 大头针的标题和子标题
    userLocation.title = "我是标题"
    userLocation.subtitle = "我是子标题☺️"
            
    // 设置用户的位置一直在地图的中心点
    // 缺陷 : 默认情况下不会放大地图的显示区域,需要手动放大
    let coordinate = userLocation.coordinate
    mapView.setCenter(coordinate, animated: true)
    }
    
    
    • 方法区别
    // 设置中心点时带动画效果
    mapView.setCenter(coordinate, animated: true)
    // 没有动画效果
    mapView.centerCoordinate = coordinate
    
    
    • userLocation - 大头针数据模型
    @available(iOS 3.0, *)
    open class MKUserLocation : NSObject, MKAnnotation {
     
        // Returns YES if the user's location is being updated.
        open var isUpdating: Bool { get }
    
        // Returns nil if the owning MKMapView's showsUserLocation is NO or the user's location has yet to be determined.
        open var location: CLLocation? { get }
     
        // Returns nil if not in MKUserTrackingModeFollowWithHeading
        @available(iOS 5.0, *)
        open var heading: CLHeading? { get }
     
        // The title to be displayed for the user location annotation.
        open var title: String?
      
        // The subtitle to be displayed for the user location annotation.
        open var subtitle: String?
    }
    
    • 上面那种定位方法有缺陷:
      • 默认情况下不会放大地图的显示区域,需要手动放大
    • 改进缺陷的方法 : 可以直接放大地图的显示区域
    extension ViewController :  MKMapViewDelegate {
        
        // 当用户位置改变时就会来到这个方法
        func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
            
    //        print("用户位置改变")
            // 大头针的标题和子标题
            userLocation.title = "我是标题"
            userLocation.subtitle = "我是子标题☺️"
            
            // 设置用户的位置一直在地图的中心点
            let coordinate = userLocation.coordinate
            mapView.setCenter(coordinate, animated: true)
            
            // span: 区域的跨度
            // 在地图上,东西经各180°,显示的区域跨度为0~360°之间
            // 南北纬各90°,显示的区域跨度为0~180°
            // 结论: 区域跨度设置的越小,那么看到的内容就越清晰
            let span = MKCoordinateSpan(latitudeDelta: 0.006, longitudeDelta: 0.004)
            // region: 区域
            // center: 地图的中心点(经度和纬度)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            mapView.setRegion(region, animated: true)
            
            /// 当区域改变时就会来到这个方法
            /// 区域改变的条件: 1.地图中心点发生改变 || 2.跨度发生改变
            ///
            /// - Parameters:
            ///   - mapView: 地图视图
            ///   - animated: 动画
            func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
                print(mapView.region.span)
            }
        }
        
         
    }
    

    效果图

    大头针1

    三.大头针的基本使用 - 添加/删除

    1.理论支撑(按照MVC的原则)

    • 在地图上操作大头针,实际上就是控制大头针的数据模型
      • 添加大头针就是添加大头针的数据模型
      • 删除大头针就是删除大头针的数据模型

    2.添加/删除大头针到地图

    • 1.确定大头针的数据模型,主要确定经纬度,只有这样才能确定大头针在地图中的位置
    • 2.之后可以确定title和subtitle
    • 3.添加到地图上

    方案一 [pass掉]

    尝试使用系统的MKUserLocation创建大头针数据模型

    • 因为点进MKUserLocation后发现,它里面有我们需要的信息,如location,title,subtitle等
    open class MKUserLocation : NSObject, MKAnnotation {
    
        open var isUpdating: Bool { get }
        open var location: CLLocation? { get }
        
        @available(iOS 5.0, *)
        open var heading: CLHeading? { get }
    
        open var title: String?
        open var subtitle: String?
    }
    
    
    • 所以使用系统的MKUserLocation创建大头针的数据模型
    // 创建大头针的数据模型
    let annotation = MKUserLocation()
    
    • 接着要设置大头针的经纬度为地图的中心位置
    // mapView是storyboard拖线进来的,地图视图
    annotation.coordinate = mapView.centerCoordinate [❌]
    
    • 结果发现的报错了,报错内容为
    // 不允许给`coordinate`这个属性赋值,因为他是不可变的
    Cannot assign to property" `coordinate` is immutable
    
    // 从上面也可以看到MKUserLocation里面的location后面有个{get},说明是只读的,不允许我们去设置
    
    • 结论: 所以我们借助系统来创建大头针无法满足我们的需求,我们要自定义大头针了

    方案二 自定义大头针

    • 遵守MKAnnotation协议,实现它里面必须实现的方法(未被optional修饰的)
    // MKAnnotation协议
    public protocol MKAnnotation : NSObjectProtocol {
    
        public var coordinate: CLLocationCoordinate2D { get }
    
        optional public var title: String? { get }
    
        optional public var subtitle: String? { get }
    }
    
    • 把这几个方法拿过来,去掉{get},说明我们即实现了协议里面的方法,又为它新增了方法,set方法
    class MTYAnnotation: NSObject , MKAnnotation {
        
        // 必须赋初值
        // 大头针的经纬度(在地图上显示的位置)
        var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
        // 大头针的标题
        var title: String?
        // 大头针的子标题    
        var subtitle: String?
    
    }
    

    代码实现

    // 点击屏幕添加大头针
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            
      	// 要求: 点击屏幕,添加大头针
       	// 1.尝试使用MKUserLocation创建大头针
      	 let annotation = MTYAnnotation()
       	// 2.设置大头针的位置
         annotation.coordinate = mapView.centerCoordinate
       	// 3.设置标题
         annotation.title = "我是标题"
    	// 4.设置子标题
       	 annotation.subtitle = "我是子标题☺️"
    	// 5.添加大头针到地图上
         mapView.addAnnotation(annotation)
            
        }
        
        // 移除大头针   
        @IBAction func removeAnnotation(_ sender: UIBarButtonItem) {
            
    	// 1.获取需要移除的大头针
         let annotations = mapView.annotations
            
    	// 2.移除大头针
         mapView.removeAnnotations(annotations)
        
        }
    

    效果图

    添加大头针1

    3.场景演练: 点击屏幕在对应位置添加大头针

    • 步骤分析
      • 首先点击屏幕,touchesBegan方法
      • 要获取在控件上点击的点
      • 将获取的点转为经纬度信息
      • 创建大头针数据模型(标题和子标题必须设置占位文字)
        • 使用自定义的大头针数据模型
      • 标注弹框中显示的城市和街道
        • 获取点的位置(经纬度信息)
        • 反地理编码
          • 判断是否有错误信息,有直接return
          • 取出地标对象
          • 更新标题和子标题

    代码实现

    // 点击屏幕添加大头针
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            
            // 1.获取在控件上点击的点
            let point = touches.first?.location(in: mapView)
            // 2.将点转为经纬度
            let coordinate = mapView.convert(point!, toCoordinateFrom: mapView)
            // 3.创建大头针数据模型,并添加到地图上
            // 标题,子标题必须设置占位文字
            let annotation = addAnnotation(coordinate: coordinate, title: " ", subtitle: " ")
            // 4.标注弹框中显示的城市和街道
            // 4.1.获取点的位置(经纬度)
            let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
            // 4.2.反地理编码
            // completionHandler后的参数:
            // clpls: 地标数组
            // error: 错误信息
            geoc.reverseGeocodeLocation(location, completionHandler: {clpls,error in
                
                // 1.判断是否有错误
                if error != nil {
                    return
                }
                // 2.取出地标对象
                // 第一个相关度最高
                guard let clpl = clpls?.first else { return }
                // 3.更新标题和子标题
                annotation.title = clpl.locality
                annotation.subtitle = clpl.name
            
            })
        }
    

    封装的添加大头针数据模型方法

    // 添加大头针
        func addAnnotation(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) -> MTYAnnotation {
            
            // 要求: 点击屏幕,添加大头针
            // 1.尝试使用MKUserLocation创建大头针数据模型
            let annotation = MTYAnnotation()
            // 2.设置大头针的位置
            annotation.coordinate = coordinate
            // 3.设置标题
            annotation.title = title
            // 4.设置子标题
            annotation.subtitle = subtitle
            // 5.添加大头针到地图上
            mapView.addAnnotation(annotation)
            // 6.返回大头针数据模型
            return annotation
            
        }
    

    移除大头针方法同上

    效果图(联网!网速慢就比较尴尬...)

    点击屏幕添加对应大头针

    4.那么系统的大头针是如何添加到地图上的呢?

    4.1 模拟系统的实现方案

    • 缺点: 无法更改大头针的样式
    // 这个方法不能设置大头针的样式
    pinAnnotationView?.image = 
    

    代理方法

    extension ViewController: MKMapViewDelegate
    {
        /// 系统的大头针是如何添加的?实现方法如下
        /// 在地图上添加一个大头针数据模型时,系统就会自动调用这个代理方法,来设置对应的大头针视图
        ///
        /// - Parameters:
        ///   - mapView: 地图视图
        ///   - annotation: 大头针数据模型
        /// - Returns: 返回创建好的大头针视图
        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            // 系统的大头针使用的是MKPinAnnotationView
            let pinID = "pinID"
            var pinAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pinID) as? MKPinAnnotationView
            if pinAnnotationView == nil {
              pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: pinID)
            }
            // 更新数据模型,防止循环引用时使用之前的模型
            pinAnnotationView?.annotation = annotation
            
            // 设置允许弹框(自己写的时候,系统默认为false了)
            pinAnnotationView?.canShowCallout = true
            // 设置大头针的颜色
            pinAnnotationView?.pinTintColor = UIColor.blue
            // 设置大头针降落动画
            pinAnnotationView?.animatesDrop = true
            // 设置大头针可以被拖拽
            pinAnnotationView?.isDraggable = true
            return pinAnnotationView
        }
        
        
        /// 当拖拽大头针的时候来到此方法
        ///
        /// - Parameters:
        ///   - mapView: 地图视图
        ///   - view: 大头针视图
        ///   - newState: 拖拽后的状态
        ///   - oldState: 拖拽前的状态
        func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, didChange newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
            
            print(oldState.rawValue, newState.rawValue)
        }
    }
    

    效果图

    模仿系统添加大头针

    4.2 自定义大头针和弹框

    • 仍旧是那个代理方法
    • 如果想要自定义大头针视图,必须使用MKAnnotationView或者自定义它的子类

    代码实现

    /// 在地图上添加大头针视图时就会来到此方法
        ///
        /// - Parameters:
        ///   - mapView: 地图视图
        ///   - annotation: 大头针视图模式
        /// - Returns: 创建好的大头针视图
        func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            // 创建标识
            let pinID = "pinID"
            // 通过标识从缓存池中取出大头针视图
            var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pinID)
            // 判断大头针视图是否存在,不存在就创建
            if annotationView == nil {
                annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: pinID)
            }
            
            // 更新大头针的数据模型
            annotationView?.annotation = annotation
            // 设置大头针的样式
            annotationView?.image = UIImage(named: "category_4")
            // 设置允许弹框
            annotationView?.canShowCallout = true
            
            // 设置弹框左边的控件
            let leftImageV = UIImageView(frame: CGRect(x: 0, y: 0,  50, height: 50))
            let leftImage = UIImage(named: "eason.jpg")
            leftImageV.image = leftImage
            annotationView?.leftCalloutAccessoryView = leftImageV
            
            // 设置弹框右边的控件
            let rightImageV = UIImageView(frame: CGRect(x: 0, y: 0,  50, height: 50))
            let rightImage = UIImage(named: "huba.jpeg")
            rightImageV.image = rightImage
            annotationView?.rightCalloutAccessoryView = rightImageV
            
            // 设置弹框底部的控件
            annotationView?.detailCalloutAccessoryView = UISwitch()
            
            return annotationView
        }
    

    效果图

    自定义大头针和弹框

  • 相关阅读:
    springMVC准确定位多个参数对象的属性
    java正则表达式应用
    mybatis与mysql插入数据返回主键
    xml文件中怎么写小于号 等特殊符号
    sqlserver 分页查询 举例
    Python报错:IndentationError: expected an indented block
    统计输入的汉字,数字,英文,other数量
    easyui+ajax获取同表关联的数据
    JAVA死锁
    mybatis自动生成mapper,dao映射文件
  • 原文地址:https://www.cnblogs.com/mtystar/p/6059357.html
Copyright © 2020-2023  润新知