定位於地圖小程序


 導讀:

       最近做了一個小程序模仿GPS,實現的基本功能有定位,地點查找,與導航等等,主要涉及到編碼與反編碼,(還有就是系統自己的)聽着很麻煩,其實當自己理解了發現也不是那么難。廢話也不多說。涉及到的函數根據代碼來介紹。

我們先看看Main.storyboard,下圖所示,結構非常簡單,幾個button,text,一個mapview。

介紹一下這個幾個按鈕的作用:

編碼:就是把我們的輸入的地點名字來獲得它對應的經緯度以及詳細信息。

反編碼:就是把經緯度進行解析,獲取這個經緯度對應的一些信息比如(城市,街道,鄉。省)。

我的位置:定位到自己的位置。

路線(導航):根據自己的位置和搜索的地名來設計路線。

在我們寫代碼前我們還需要做幾件事:

1.授權:打開工程中info.plist屬性列表文件,添加2個鍵Privacy - Location Usage Description和Privacy - Location Always Usage Description。

2添加2個框架:如下圖

在這里介紹一下這2框架,為什么我們要用到它:

CoreLocation框架:在IOS中,定位服務API主要使用的就是CoreLocation框架,在我們編制定位程序時,就要用到它的3個類。

1)CLLocationManager:他的作用在於可以為我們提供位置的一些信息(經緯度),還可以根據它來監控當時人所在的區域,前進方向。

2)CLLocationManagerDelegate:這是一個委托協議,我們在定位時需要用到它的一些方法。(下面在詳細介紹)

3)CLLocation:封裝了位置和高度信息。

MapKit框架:在IOS開發中,MKMapView類是開發地圖應用的核心。使用Map Kit API就要導入MapKit框架。

-------這些都是小編我的自己感悟,並不代表全部,小編不可能把概念,功能全部寫下來,如果想了解更加詳細。請去網上查資料

目前介紹到這里,接下來進入代碼環節

首先自定一個標注類:用來顯示地址的信息:

MyAnnotation.h

 

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

@interface MyAnnotation : NSObject<MKAnnotation>
@property (nonatomic,readwrite)CLLocationCoordinate2D coordinate;//地理坐標
@property(nonatomic,copy) NSString * street;//街道信息
@property(nonatomic,copy) NSString * city;//城市
@property(nonatomic,copy) NSString * state;//州,省,市;
@property(nonatomic,copy) NSString * zip;  //郵編

MyAnnotation.m

#import "MyAnnotation.h"

@implementation MyAnnotation

-(NSString *)title{
     return @"您的位置";
}
-(NSString *)subtitle{
    NSMutableString *ret=[NSMutableString new];
    if (_state) {
        [ret appendString:_state];
    }
    if (_city) {
        [ret appendString:_city];
    }
    if (_city&&_state) {
        [ret appendString:@","];
    }
    if (_street&&(_city||_state||_city)) {
        [ret appendString:@"~"];
    }
    if (_street) {
        [ret appendString:_street];
    }
    if (_zip ) {
        [ret appendFormat:@",%@",_zip];
    }
    return ret;
}
@end

 

ViewController.h

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#import "MyAnnotation.h"

@interface ViewController : UIViewController
//地名
@property (weak, nonatomic) IBOutlet UITextField *txtKey;
//編碼
- (IBAction)geocdeQuery:(id)sender;
//MkMapview 顯示地圖
@property (weak, nonatomic) IBOutlet MKMapView *mapview;
//我的位置
- (IBAction)reverseGeoccode:(id)sender;
//經度
@property (weak, nonatomic) IBOutlet UITextField *JingDuText;
//緯度
@property (weak, nonatomic) IBOutlet UITextField *WeiDuText;
//反編碼
- (IBAction)reverseGeoccodeTwo:(id)sender;
//路線(導航)
- (IBAction)geocdeQueryTwo:(id)sender;
@end

 定義的幾個控件與Main.storyboard一致的可以結合着看

#import <CoreLocation/CoreLocation.h>引入了CLLocation模塊。
#import <MapKit/MapKit.h>我們添加MapKit框架

#import "MyAnnotation.h"我們自定一的一個類(標注)

讓我們來看看viewcontroller.m的代碼
@interface ViewController ()<CLLocationManagerDelegate>

@property(nonatomic,strong)CLLocationManager *locationmanger;
@property(nonatomic,strong)CLLocation *currlocation;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
        _mapview.mapType=MKMapTypeStandard;//標注地圖類型
    
//    [_mapview setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
//    _mapview.userLocation.title=@"您的位置:";
//    _mapview.userLocation.subtitle=@"xxxxxxxxxxx";
    
        _mapview.delegate=self;//
    

    self.locationmanger=[[CLLocationManager alloc]init];
    //設置委托對象為自己
    self.locationmanger.delegate=self;
    //要求CLLocationManager對象的返回結果盡可能的精准
    self.locationmanger.desiredAccuracy=kCLLocationAccuracyBest;
    //設置距離篩選器distanceFilter,下面表示設備至少移動1000米,才通知委托更新
    self.locationmanger.distanceFilter=1000.0f;
    
    //使用中授權
    [self.locationmanger requestWhenInUseAuthorization];
    //永久授權
    [self.locationmanger requestAlwaysAuthorization];
    
    [self.locationmanger startUpdatingLocation];
}
 
        
CLLocationManagerDelegate申明遵守的協議。
地圖顯示的類型共有3種,他們是在枚舉類型MKMapType中定義的(Standard)標注地圖類型,(Satellite)衛星地圖類型,(Hybrid)混合地圖類型。
_mapview.delegate=self:將當前試圖控制器負值給地圖視圖的delegate屬性。這樣地圖視圖會回掉viewcontroller,如果失敗- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error調用這個方法;
//回調viewcontroller失敗:
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error{
    NSLog(@"error:%@",[error description]);
}

 

[self.locationmanger startUpdatingLocation];//開始定位

 開始定位后就要檢查權限,是否允許定位服務:

//CLAuthorizationStatus枚舉是定位的時候關於授權狀態的一個枚舉
-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
    NSLog(@"status----->%d",status);
    if (status == kCLAuthorizationStatusAuthorizedAlways) {
        NSLog(@"定位服務授權狀態已經被用戶允許在任何狀態下獲取位置信息。一直開啟定位");
    }else if (status == kCLAuthorizationStatusAuthorizedWhenInUse){
        NSLog(@"定位服務授權狀態僅被允許在使用應用程序的時候,當使用時開啟定位");
    }else if (status == kCLAuthorizationStatusDenied){
        NSLog(@"定位服務授權狀態已經被用戶明確禁止,或者在設置里的定位服務中關閉");
    }else if (status == kCLAuthorizationStatusRestricted){
        NSLog(@"無法使用定位服務,該狀態用戶無法改變");
    }else if (status == kCLAuthorizationStatusNotDetermined){
        NSLog(@"用戶尚未做出決定是否啟用定位服務,用戶從未選擇過權限");
    }
    
    
}

如果我們的定位服務已經開啟,也設置了CLLocationManagerDelegate的屬性delegate,就會回掉委托方法,如果定位失敗:

-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
    NSLog(@"定位失敗:%@",error );
}

定位成功:

//定位成功
- (void)locationManager:(CLLocationManager *)manager
     didUpdateLocations:(NSArray<CLLocation *> *)locations{
    //取出數組的第一個信息
    _currlocation=[locations firstObject];
    //停止定位
    [self.locationmanger stopUpdatingLocation];
    //經緯度
    NSString *lati=[NSString stringWithFormat:@"%3.5f",_currlocation.coordinate.latitude];
    NSString *longi=[NSString stringWithFormat:@"%3.5f",_currlocation.coordinate.longitude];
    //打印出經緯度
    NSLog(@"========%@---%@============",lati,longi);
    
    //反編碼
    CLGeocoder*geocoder=[[CLGeocoder alloc]init];
    [geocoder reverseGeocodeLocation:_currlocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        //        if ([placemarks count]>0) {
        
        CLPlacemark *placemark=placemarks[0];
        //地標字典,從地標集合中取出一個地標對象,循環便利就好
        NSDictionary *addressDictionary=placemark.addressDictionary;
        //從字典中取出街道。城市,省等信息
        NSString *address=[addressDictionary objectForKey:(NSString*)NSTextCheckingStreetKey];
        address=address==nil ? @"" :address;
        NSString *state=[addressDictionary objectForKey:(NSString *)NSTextCheckingStateKey];
        state=state==nil ? @"" :state;
        NSString *city=[addressDictionary objectForKey:(NSString *)NSTextCheckingCityKey];
        city=city==nil ? @"" :city;
        
        //把街道,城市,省 等一些信息組合起來
        NSString *allname=[NSString stringWithFormat:@"%@%@%@",state,address,city];
        //用MKCoordinateRegionMakeWithDistance建立一個結構體,調整地圖位置和縮放比例
        MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(placemark.location.coordinate, 10000, 10000);
        [_mapview setRegion:viewRegion animated:YES];//顯示動畫
        //標注顯示出來
        MyAnnotation*annotation=[[MyAnnotation alloc]init];
        annotation.state=allname;
        annotation.coordinate=placemark.location.coordinate;
        [_mapview addAnnotation:annotation];
    
    
     }];
    
}

這串代碼其實也不用介紹什么了小編在在注釋里都寫的非常清楚了。提幾個注意的

在這串代碼中我用的是反編碼,是通過經緯度查找地點的信息,

reverseGeocodeLocation: completionHandler(反編碼)

[_mapview addAnnotation:annotation];把annotation添加到地圖視圖上,這個方法一單被調用會回掉-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation

 

//在地圖上添加標注時回調
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
    
    MKPinAnnotationView*annnotayionview=(MKPinAnnotationView *)[_mapview dequeueReusableAnnotationViewWithIdentifier:@"PIN_ANNOTATION"];
    if (annnotayionview==nil) {
        annnotayionview=[[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:@"PIN_ANNOTATION"];
    }
    //黑色
    annnotayionview.pinTintColor=[UIColor blackColor];
    //是否動畫
    annnotayionview.animatesDrop=YES;
    //點擊大頭針會出現一個氣泡,這個氣泡回現實當前地址的信息
    annnotayionview.canShowCallout=YES;
    
    return annnotayionview;
    
}

紅色部分代碼的意思就是用dequeueReusableAnnotationViewWithIdentifier方法通過一個可重用的標識符“PIN_ANNOTATION”獲得MKPinAnnotationView對象。在判斷這個對象是否存在,如果不在(==nil)就重新創建一個

initWithAnnotation:reuseIdentifier(通過這個函數創建)

現在就來運行一下看看效果如何:


 好了目前實現一個基本的定位功能已經實現了

我們來實現我們的第一個button(編碼) 的功能:通過輸入的地名來查詢

- (IBAction)geocdeQuery:(id)sender {
    if (_txtKey.text==nil|| _txtKey.text.length==0) { //判斷輸入的地址是否符合規定
        return;
    }
   // [self.locationmanger startUpdatingLocation];
    CLGeocoder*geocoder=[[CLGeocoder alloc]init];// 聲明GLGeocoder對象並初始化
    //開始編碼
    [geocoder geocodeAddressString:_txtKey.text completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        NSLog(@"------->%lu",[placemarks count]);
        
        if ([placemarks count]>0) {
            [_mapview removeAnnotations:_mapview.annotations];
            for (int i=0; i<[placemarks count]; i++) {
                CLPlacemark *placemark=placemarks[i];
                //用MKCoordinateRegionMakeWithDistance建立一個結構體,調整地圖位置和縮放比例
                MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(placemark.location.coordinate, 10000, 10000);
                [_mapview setRegion:viewRegion animated:YES];//設置動畫效果
                
                MyAnnotation*annotation=[[MyAnnotation alloc]init];
                annotation.street=placemark.thoroughfare;
                annotation.city=placemark.locality;
                annotation.state=placemark.administrativeArea;
                annotation.zip=placemark.postalCode;
                annotation.coordinate=placemark.location.coordinate;
                
                [_mapview addAnnotation:annotation];//把annotation添加到地圖視圖上,這個方法一單被調用會回掉-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
                //獲取地標經緯度信息
                CLLocationCoordinate2D coordinate=placemark.location.coordinate;
                //從text中顯示出來
                self.JingDuText.text=[NSString stringWithFormat:@"%3.5f",coordinate.latitude];
                self.WeiDuText.text=[NSString stringWithFormat:@"%3.5f",coordinate.longitude];
            }
        }
    }];
}

geocodeAddressString:completionHandler;方法進行地理信息編碼查詢,查詢的結果placemarks是一個NSArray類型

在上面我們介紹了一下反編碼,現在我來介紹一下編碼

geocodeAddressDictionary:completionHandler: 通過指定一個地址信息字典對象參數進行查詢

geocodeAddressString:completionHandler:通過指定一個地址信息字符串參數進行查詢

geocodeAddressString:inRegion:completionHandler:通過制定地址信息字符串和查詢范圍進行查詢

 [_mapview removeAnnotations:_mapview.annotations]:移除地圖上的標記點,否則反復查詢地圖上的標記點會越來越多

 

第二個button(反編碼)的功能

- (IBAction)reverseGeoccodeTwo:(id)sender {
      //把經緯度提取出來
      CLLocation *myLocal=[[CLLocation alloc]initWithLatitude:[self.JingDuText.text doubleValue] longitude:[self.WeiDuText.text doubleValue]];
    CLGeocoder *grocode=[[CLGeocoder alloc]init];
    
    [grocode reverseGeocodeLocation:myLocal completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        
        
        CLPlacemark *placemark=placemarks[0];
        
        NSDictionary *addressDictionary=placemark.addressDictionary;
        
        NSString *address=[addressDictionary objectForKey:(NSString*)NSTextCheckingStreetKey];
        address=address==nil ? @"" :address;
        NSString *state=[addressDictionary objectForKey:(NSString *)NSTextCheckingStateKey];
        state=state==nil ? @"" :state;
        NSString *city=[addressDictionary objectForKey:(NSString *)NSTextCheckingCityKey];
        city=city==nil ? @"" :city;
        
        
        NSString *allname=[NSString stringWithFormat:@"%@%@%@",state,address,city];
        self.txtKey.text=city;
        MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(placemark.location.coordinate, 10000, 10000);
        [_mapview setRegion:viewRegion animated:YES];
        
        MyAnnotation*annotation=[[MyAnnotation alloc]init];
        annotation.state=allname;
        annotation.coordinate=placemark.location.coordinate;
        [_mapview addAnnotation:annotation];

    
    }];
    }

第3個buttom(路線(導航))的功能

- (IBAction)geocdeQueryTwo:(id)sender {
    if (_txtKey.text==nil|| _txtKey.text.length==0) { //判斷輸入的地址是否符合規定
        return;
    }
    //CLGeocoder類中有幾個方法,一個是把經緯度轉化成大家能看懂的信息,比如:city,county,街道等等(反編碼),CLGeocoder類中的其他幾個方法也可以把city,county等信息直接轉化為坐標(編碼)
    CLGeocoder*geocoder=[[CLGeocoder alloc]init];// 聲明GLGeocoder對象並初始化
    //開始編碼
    [geocoder geocodeAddressString:_txtKey.text completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        NSLog(@"------->%lu",[placemarks count]);
        
        if ([placemarks count]>0) {
            CLPlacemark *placemark=placemarks[0];
            //獲得該地點的坐標
            CLLocationCoordinate2D coordinate=placemark.location.coordinate;
            //addressDictionary是該地點的信息通過placemark.addressDictionary獲得
            NSDictionary *adress=placemark.addressDictionary;
            //實例化MKPlacemark對象initWithCoordinate的參數是坐標
            MKPlacemark *place=[[MKPlacemark alloc]initWithCoordinate:coordinate addressDictionary:adress];
            
            //設置路線
            NSDictionary *options=[[NSDictionary alloc]initWithObjectsAndKeys:MKLaunchOptionsDirectionsModeDriving,MKLaunchOptionsDirectionsModeKey, nil];
             MKMapItem *mapItme2=[[MKMapItem alloc]initWithPlacemark:place];
             [mapItme2 openInMapsWithLaunchOptions:options];
            
            
        }
        }];
}

 MKMapItem *mapItme2=[[MKMapItem alloc]initWithPlacemark:place]

實例化MkMapItme MkMapItme類封裝了地圖上一個點的信息類。我們需要在地圖上顯示的點封裝到MKMApItme對象中,構造器initWithPlacemark:的參數是MKPlacemark類型
[mapItme2 openInMapsWithLaunchOptions:options]
開啟iOS系統自帶的地圖,並用openInMapsWithLaunchOptions方法實例化mapItme2,它的參數是NSDictionary,它可以告訴地圖顯示哪些信息。

MKLaunchOptionsDirectionsModeKey(路線模式)它有2個值【MKLaunchOptionsDirectionsModeDriving】開車路線【MKLaunchOptionsDirectionsModeWalking】步行路線

MKLaunchOptionsMapTypeKey(地圖類型)

MKLaunchOptionsMapCenterKey(地圖中心點)

MKLaunchOptionsMapSpanKey(地圖跨度)

MKLaunchOptionsShowsTrafficKey(交通狀況)

第四個button(我的位置)功能

 

- (IBAction)reverseGeoccode:(id)sender {
        [self.locationmanger startUpdatingLocation];

}

 

好了到了目前為止一個小的定位程序做好了運行一下程序吧:測試一下吧

目前定位到重慶市,在text中輸入beijing點擊一下路線(導航)按鈕看看什么效果吧

  點擊后

 

程序可以正確運。

這4個按鈕的功能都是獨立的,他們之間沒有聯系。其中的代碼幾乎一樣,所以我也就沒有過多的解釋(其實是我比較懶😄)這是我的學習筆記希望對你們有幫助。

------------------------注:以上代碼僅限參考。

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM