iOS开发之封装高德地图搜索管理类

一、前言

我们在项目开发中经常会用到第三方的SDK等来实现项目所需要的功能,很多时候拿到别人的东西并不是所有的都适合我们自己的项目,为了方便使用或者扩展更多功能,或者为了对第三方的代码进行更合理的管理,经常要对他们的代码和功能进行封装。以此来达到方便管理,更便于阅读,功能更适合自己项目等功能,而且有些东西进行一次封装,多处可以使用,这样就很方便。

我们以高德地图搜索类AMapSearchAPI为例子,进行该功能的封装和在项目中的使用。

二、高德地图搜索管理类AMapSearchAPI

AMapSearchAPI里提供了各种搜索功能的Api,比如关键字查询Api:

/**
 * @brief POI 关键字查询接口
 * @param request 查询选项。具体属性字段请参考 AMapPOIKeywordsSearchRequest 类。
 */
- (void)AMapPOIKeywordsSearch:(AMapPOIKeywordsSearchRequest *)request;

还有各种周边查询、逆地理编码查询等等,这些Api都有各自的代理回调,比如关键字检索回调代理:

/**
 * @brief POI查询回调函数
 * @param request  发起的请求,具体字段参考 AMapPOISearchBaseRequest 及其子类。
 * @param response 响应结果,具体字段参考 AMapPOISearchResponse 。
 */
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response;

提供了各种搜索功能和回调代理。

三、功能实现

这里考虑到项目中经常使用,所以创建一个单例作为项目中高德地图搜索管理类。

1、实现代码

DDSearchManager.h

#import <Foundation/Foundation.h>

typedef void(^keyWordSearchBlock)(NSMutableArray *pointAnnotaions);//用于关键字检索回调数据
typedef void(^tipsSearchBlock)(NSArray *tips);//用于tip搜索回调数据

@interface DDSearchManager : NSObject

+ (instancetype)sharedManager;

/* 2比1查询速度快;1的数据量比2大 。*/

/*
 1、关键字检索
 
 keyword为检索关键字;city可为空;block返回检索完后的数组,数组中是MAPointAnnotation的对象
 
 注意:关键字未设置城市信息(默认为全国搜索)时,如果涉及多个城市数据返回,仅会返回建议城市,请根据APP需求,选取城市进行搜索。
 */
- (void)keyWordsSearch:(NSString *)keyword city:(NSString *)city returnBlock:(keyWordSearchBlock)block;


/*
 2、输入提示查询
 
 block回调查询后的数组,该数组里是AMapTip对象
 */
- (void)inputTipsSearch:(NSString *)tips city:(NSString *)city returnBlock:(tipsSearchBlock)block;

@end

DDSearchManager.m

#import "DDSearchManager.h"

#import <AMapSearchKit/AMapSearchKit.h>
#import <MAMapKit/MAMapKit.h>

@interface DDSearchManager ()<AMapSearchDelegate>

//高德地图搜索管理类
@property (nonatomic, strong) AMapSearchAPI *searchAPI;

@property (nonatomic, copy) keyWordSearchBlock keyWordSearchBlock;
@property (nonatomic, copy) tipsSearchBlock tipSearchBlock;

@end

@implementation DDSearchManager

+ (instancetype)sharedManager {
    static DDSearchManager *manager = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        manager = [[self alloc]init];
    });
    return manager;
}

/// 关键字查询
- (void)keyWordsSearch:(NSString *)keyword city:(NSString *)city returnBlock:(keyWordSearchBlock)block
{
    if (keyword.length) {
        
        self.keyWordSearchBlock = block;
        
        AMapPOIKeywordsSearchRequest *request = [[AMapPOIKeywordsSearchRequest alloc]init];
        
        request.keywords = keyword;
        if (city.length) {
            request.city = city;
            /* 搜索SDK 3.2.0 中新增加的功能,只搜索本城市的POI。*/
            request.cityLimit = YES;
        }
        /*返回扩展信息*/
        request.requireExtension = YES;
        request.requireSubPOIs = YES;
        
        /*发起关键字搜索*/
        [self.searchAPI AMapPOIKeywordsSearch:request];
    }
}

/// 输入提示查询
- (void)inputTipsSearch:(NSString *)tips city:(NSString *)city returnBlock:(tipsSearchBlock)block {
    if (tips.length) {
        
        self.tipSearchBlock = block;
        
        AMapInputTipsSearchRequest *request = [[AMapInputTipsSearchRequest alloc]init];
        
        request.keywords = tips;
        if (city.length) {
            request.city = city;
            request.cityLimit = YES;
        }
        
        /*发起关键字搜索*/
        [self.searchAPI AMapInputTipsSearch:request];
    }
}

#pragma mark delegate
#pragma AMapSearchDelegate
//关键字查询代理回调
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response
{
    if (response.pois.count == 0) {
        return;
    }
    
    NSMutableArray *poiAnnitations = [[NSMutableArray alloc]init];
    
    [response.pois enumerateObjectsUsingBlock:^(AMapPOI * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        MAPointAnnotation *annotation = [[MAPointAnnotation alloc] init];
        [annotation setCoordinate:CLLocationCoordinate2DMake(obj.location.latitude, obj.location.longitude)];
        [annotation setTitle:obj.name];
        [annotation setSubtitle:obj.address];
        
        [poiAnnitations addObject:annotation];
    }];
    //回调检索结果,回调数组是MAPointAnnotation的对象集合
    if (self.keyWordSearchBlock) {
        self.keyWordSearchBlock (poiAnnitations);
    }
}
//输入提示查询代理回调
-(void)onInputTipsSearchDone:(AMapInputTipsSearchRequest *)request response:(AMapInputTipsSearchResponse *)response
{
    if (self.tipSearchBlock)
    {
        NSMutableArray *arr = [NSMutableArray array];
        for (int i=0; i<response.tips.count; i++)
        {
            //输入提示类
            AMapTip *tip = [response.tips objectAtIndex:i];
            
            if (tip.location.latitude!=0 && ![tip.uid isEqualToString:@""])
            {
                [arr addObject:tip];
            }
        }
        //回调tip检索结果,回调数组中是AMapTip对象的集合。
        self.tipSearchBlock (arr);
    }
}

#pragma mark set/get
- (AMapSearchAPI *)searchAPI {
    if (!_searchAPI) {
        _searchAPI = [[AMapSearchAPI alloc]init];
        _searchAPI.delegate = self;
    }
    return _searchAPI;
}

@end

2、调用

这里随便截取了一段示例代码,发起tip检索,然后回调数据,更新数据源,然后刷新tableView的列表功能。

[[DDSearchManager searchManager] inputTipsSearch:text city:self.selectCityBtn.titleLabel.text returnBlock:^(NSArray *tips) {

    [_searchArr removeAllObjects];
    [_searchArr addObjectsFromArray:tips]];
    [_tableView reloadData];
}];

这里有一个问题就是刷新tableView的时候如果给cell赋值,你得知道回调数组里面是什么类型的数据,不然不知道怎么赋值。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellId = kCellReuseIdentifier;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellReuseIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellReuseIdentifier];
    }
    
    AMapPOI *poi = self.dataSource[indexPath.row];
    NSLog(@"poi.name :%@     poi.address:%@ ",poi.name,poi.address);
    cell.textLabel.text = poi.name;
    cell.detailTextLabel.text = poi.address;

    return cell;
}

四、问题思考

以上一个高德地图的搜索管理类就简单的完成了,但是就这样能算是封装吗?我觉得不能算完全封装,原因有如下:

1、结构都通过数据回调,使用的人怎么会知道数组里是什么呢?
2、封装的话一般不应该将原来的东西暴露在外,这里虽然DDSearchManager.h没有暴露有关于高德地图SDK,AMapSearchAPI等,但是在使用的时候,还得用高德SDK中的类AMapTip和MAPointAnnotation,这样的封装不够彻底。

所以以上封装还算不到封装,如果算也只是个半成品,不够彻底。所以我们需要继续优化,继续封装。

五、继续封装

1、创建一个定义搜索结果的基础数据类型的类DDSearchObj

DDSearchObj.h
//
//  DDSearchObj.h
//  GDAddressSelected
//  该文件定义了搜索结果的基础数据类型。
//

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

@interface DDSearchObj : NSObject

@end

///输入提示
@interface DDSearchTip : DDSearchObj

///名称
@property (nonatomic, copy) NSString   *name;
///区域编码
@property (nonatomic, copy) NSString   *adcode;
///所属区域
@property (nonatomic, copy) NSString   *district;
///地址
@property (nonatomic, copy) NSString   *address;
///经纬度
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;

@end

///点标注数据
@interface DDSearchPointAnnotation : DDSearchObj

///经纬度
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
///标题
@property (nonatomic, copy) NSString *title;
///副标题
@property (nonatomic, copy) NSString *subtitle;

@end

这个类里面有DDSearchTip和DDSearchPointAnnotation,这分别是tip搜索回调数据类型的类和关键字搜索回调数据的类,这两个类有各自的属性,视项目情况自己添加更多属性等。

DDSearchObj.m
#import "DDSearchObj.h"

@implementation DDSearchObj

@end


@implementation DDSearchPointAnnotation

@end


@implementation DDSearchTip

@end

2、优化DDSearchManager类

DDSearchManager.h
#import <Foundation/Foundation.h>
#import "DDSearchObj.h"

//关键字搜索数据回调block
typedef void(^keyWordSearchBlock)(NSArray <__kindof DDSearchPointAnnotation*> *pointAnnotaions);
//tip搜索数据回调block
typedef void(^tipsSearchBlock)(NSArray <__kindof DDSearchTip*> *tips);

@interface DDSearchManager : NSObject

+ (instancetype)sharedManager;

/* 2比1查询速度快;1的数据量比2大 。*/

/*
 1、关键字检索
 
 keyword为检索关键字;city可为空;block返回检索完后的数组,数组中是MAPointAnnotation的对象
 
 注意:关键字未设置城市信息(默认为全国搜索)时,如果涉及多个城市数据返回,仅会返回建议城市,请根据APP需求,选取城市进行搜索。
 */
- (void)keyWordsSearch:(NSString *)keyword city:(NSString *)city returnBlock:(keyWordSearchBlock)block;

/*
 2、输入提示查询
 
 block回调查询后的数组,该数组里是AMapTip对象
 */
- (void)inputTipsSearch:(NSString *)tips city:(NSString *)city returnBlock:(tipsSearchBlock)block;

@end

引入DDSearchObj.h头文件,将数据回调的block用<__kindof DDSearchPointAnnotation*>修饰,表示该数组里面只能是DDSearchPointAnnotation类型的数据,这样的话,当使用该类的人用此方法的时候block回调里面就会知道,这个回调数据是DDSearchPointAnnotation的数组,而不是其他什么东西的数据。

DDSearchManager.m
#import "DDSearchManager.h"
#import <AMapSearchKit/AMapSearchKit.h>
#import <MAMapKit/MAMapKit.h>

@interface DDSearchManager ()<AMapSearchDelegate>

@property (nonatomic, strong) AMapSearchAPI *searchAPI;

@property (nonatomic, copy) keyWordSearchBlock keyWordSearchBlock;
@property (nonatomic, copy) tipsSearchBlock tipSearchBlock;

@end

@implementation DDSearchManager

+ (instancetype)sharedManager {
    static DDSearchManager *manager = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        manager = [[self alloc]init];
    });
    return manager;
}

/// 关键字查询
- (void)keyWordsSearch:(NSString *)keyword city:(NSString *)city returnBlock:(keyWordSearchBlock)block
{
    if (keyword.length) {
        
        self.keyWordSearchBlock = block;
        
        AMapPOIKeywordsSearchRequest *request = [[AMapPOIKeywordsSearchRequest alloc]init];
        
        request.keywords = keyword;
        if (city.length) {
            request.city = city;
            /* 搜索SDK 3.2.0 中新增加的功能,只搜索本城市的POI。*/
            request.cityLimit = YES;
        }
        /*返回扩展信息*/
        request.requireExtension = YES;
        request.requireSubPOIs = YES;
        
        /*发起关键字搜索*/
        [self.searchAPI AMapPOIKeywordsSearch:request];
    }
}

/// 输入提示查询
- (void)inputTipsSearch:(NSString *)tips city:(NSString *)city returnBlock:(tipsSearchBlock)block {
    if (tips.length) {
        
        self.tipSearchBlock = block;
        
        AMapInputTipsSearchRequest *request = [[AMapInputTipsSearchRequest alloc]init];
        
        request.keywords = tips;
        if (city.length) {
            request.city = city;
            request.cityLimit = YES;
        }
        
        /*发起关键字搜索*/
        [self.searchAPI AMapInputTipsSearch:request];
    }
}

#pragma mark delegate
#pragma AMapSearchDelegate
//关键字查询代理回调
- (void)onPOISearchDone:(AMapPOISearchBaseRequest *)request response:(AMapPOISearchResponse *)response
{
    if (response.pois.count == 0) {
        return;
    }
    
    NSMutableArray *poiAnnitations = [[NSMutableArray alloc]init];
    
    [response.pois enumerateObjectsUsingBlock:^(AMapPOI * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
//        MAPointAnnotation *annotation = [[MAPointAnnotation alloc] init];
//        [annotation setCoordinate:CLLocationCoordinate2DMake(obj.location.latitude, obj.location.longitude)];
//        [annotation setTitle:obj.name];
//        [annotation setSubtitle:obj.address];
        
        DDSearchPointAnnotation *annotation = [[DDSearchPointAnnotation alloc] init];
        [annotation setCoordinate:CLLocationCoordinate2DMake(obj.location.latitude, obj.location.longitude)];
        [annotation setTitle:obj.name];
        [annotation setSubtitle:obj.address];
        
        [poiAnnitations addObject:annotation];
    }];

    if (self.keyWordSearchBlock) {
        self.keyWordSearchBlock (poiAnnitations);
    }
}
//输入提示查询代理回调
-(void)onInputTipsSearchDone:(AMapInputTipsSearchRequest *)request response:(AMapInputTipsSearchResponse *)response
{
    if (self.tipSearchBlock)
    {
        NSMutableArray *arr = [NSMutableArray array];
        for (int i=0; i<response.tips.count; i++)
        {
            AMapTip *tip = [response.tips objectAtIndex:i];
            
//            if (tip.location.latitude!=0 && ![tip.uid isEqualToString:@""])
//            {
//                [arr addObject:tip];
//            }
            
            if (tip.location.latitude!=0 && ![tip.uid isEqualToString:@""])
            {
                DDSearchTip *ddTip = [[DDSearchTip alloc] init];
                ddTip.name = tip.name;
                ddTip.adcode = tip.adcode;
                ddTip.district = tip.name;
                ddTip.address = tip.address;
                ddTip.coordinate = CLLocationCoordinate2DMake(tip.location.latitude, tip.location.longitude);
                
                [arr addObject:tip];
            }
            
        }
        self.tipSearchBlock (arr);
    }
}

#pragma mark set/get
- (AMapSearchAPI *)searchAPI {
    if (!_searchAPI) {
        _searchAPI = [[AMapSearchAPI alloc]init];
        _searchAPI.delegate = self;
    }
    return _searchAPI;
}

@end

这里我将原来的写法注释掉,稍作了变动。将高德地图的数据转化成我们自己的数据存储,并回调。这样的话,在外部使用的时候,用的人不用关心高德地图类里面的实现过程,中需要知道当发起检索时回调,回调的数据在相应的地方去拿数据就好了。

3、发起检索

[[DDSearchManager sharedManager] keyWordsSearch:@"北京大学" city:@"北京" returnBlock:^(NSArray<__kindof DDSearchPointAnnotation *> *pointAnnotaions) {
    
    for (DDSearchPointAnnotation *annotation in pointAnnotaions)
    {
        NSLog(@"%@,%@,%f,%f",annotation.title,annotation.subtitle,annotation.coordinate.latitude,annotation.coordinate.longitude);
    }
}];

这里当我们调用该方法进行关键字检索的时候,在block回调中就能知道回调的数据是DDSearchPointAnnotation对象的数组,不用关心高德地图内部检索的结果是什么,只需要进入DDSearchPointAnnotation类里,就可以知道回调的数据你需要取哪些值。

这样避免了高德SDK类的外部暴露出来,这就简单的封装了一个高德地图检索的管理类,这里是用block进行数据回调的,当然小伙伴们也可以用代理的方式。

六、总结

  • 当我们封装一个第三方的功能的时候,封装使得它能够更好的服务于我们自己的项目。

  • 如果要封装尽量封装的彻底一点,尽量不要暴露第三方的东西,你的封装类依赖于三方,实现的时候还依赖于三方类,这样的封装总感觉不是很完美。

  • 当然有的小伙伴说了不进行上面的优化封装部分也行,也能用,就看个人怎么理解了。

代码:https://github.com/Mexiang/DDSearchManager

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容