iOS实现模拟定位功能的示例代码

前言

App中越来越多的功能依赖用户实际的位置,例如基于用户位置提供推荐数据、基于定位判断某些功能是否可用,但是在开发调试中XCode却没有提供自定义的模拟定位的功能,所以本文主要的目的是现实一个可以在开发调试过程中随时模拟定位的功能。

思路

我们在iOS的app开发中通常采用的是CLLocationManager来获取用户当前的位置,当然也可以采用MKMapView的showUserLocation来获取用户的位置,所以我们分别针对这两种情况分析。

CLLocationManager

采用CLLocationManager获取定位时,是根据CLLocationManagerDelegate中- (void)locationManager:(CLLocationManager *)manager   didUpdateLocations:(NSArray<CLLocation *> *)locations的回调来获取到定位的。我们只需要在系统回调这个方法传递给业务代码的中间,插入一部分代码,来修改locations参数。原本的逻辑为系统回调->业务代码,现在变为系统回调->模拟定位模块->业务代码,就实现了无侵入式的实现模拟定位功能。为了实现这个逻辑,可以有以下几个思路。

1、 Runtime swizzle

因为业务代码是根据- (void)locationManager:(CLLocationManager *)manager   didUpdateLocations:(NSArray<CLLocation *> *)locations方法来接受回调的,所以可以采用Runtime swizzle这个方法,来实现模拟定位的功能,但是我们中间组件是不知道业务代码中具体是哪个类,所以无法具体指定runtime swizzle哪个类,所以只能遍历所有的类,判断当前类的方法列表中是否有locationManager:didUpdateLocations:这个方法,如果存在则swizzle。

  • 优点:便于理解。
  • 缺点:需要遍历所有的类和类的方法列表。

2、中间代理对象

这种思路是Swizzle了CLLocationManager的setDelegate:方法,当调用setDelegate时,将真实的delegate object保存下来,再将我们定义的中间代理类swizzle delegate对象设置为CLLocationManager的delegate,这样当系统回调CLLocationManagerDelegate,会先回调到中间代理类swizzle delegate中,再由swizzle delegate将事件传递到真实的delegate object。

  • 优点:相对于第一种方法,不需要遍历类和类的方法列表,只需swizzle CLLocationManager中的setDelegate:方法即可。
  • 缺点:在中间代理类swizzle delegate中需要实现全部的CLLocationManagerDelegate方法,如果后续增加代理方法,仍需要修改这个类。

3、采用NSProxy实现中间代理对象

Objective-C中有2个基类,常用的就是NSObject,另一个就是NSProxy,NSProxy主要用于消息转发处理,所以采用NSProxy我们可以更好的处理方法二中的缺点。

3.1创建一个新的类MockLocationProxy,集成自NSProxy。

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

@interface MockLocationProxy : NSProxy

@property (nonatomic, weak, readonly, nullable) id <CLLocationManagerDelegate> target;

- (instancetype)initWithTarget:(id <CLLocationManagerDelegate>)target;

@end
// MockLocationProxy.m
#import "MockLocationProxy.h"

@implementation MockLocationProxy

- (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target {
  _target = target;
  return self;
}

@end

接着就来处理消息转发的逻辑,首先我们要知道我们想要的是什么效果,系统回调给MockLocationProxy,MockLocationProxy只处理locationManager:didUpdateLocations:,其他的消息都仍然交给原target。

所以我们在MockLocationProxy.m中添加以下方法:

// MockLocationProxy.m
@implementation MockLocationProxy

- (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target {
  _target = target;
  return self;
}

- (BOOL)respondsToSelector:(SEL)aSelector {
  if (aSelector == @selector(locationManager:didUpdateLocations:)) {
    return YES;
  }
  return [self.target respondsToSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
  SEL sel = invocation.selector;
  if ([self.target respondsToSelector:sel]) {
    [invocation invokeWithTarget:self.target];
  }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
  return [self.target methodSignatureForSelector:sel];
}

#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
  if ([self.target respondsToSelector:_cmd]) {
    // 模拟定位代码
    CLLocation *mockLocation = [[CLLocation alloc] initWithLatitude:39.908722 longitude:116.397499];
    locations = @[mockLocation];
    [self.target locationManager:manager didUpdateLocations:locations];
  }
}
@end

当消息发送给MockLocationProxy时,判断当前方法是否是locationManager:didUpdateLocations:,如果是,则MockLocationProxy响应事件,否则直接传递给原本的target。到此已经可以随时处理模拟定位。你只需要在模拟定位的代码做一些处理,就可以随时修改定位。

One more.

上述方法虽然可以模拟定位,但是每次修改模拟值都需重新build,那么有没有办法在运行时随时修改这个值呢?

LLDebugTool

当然可以,你只需要在你的项目中集成LLDebugTool,调用其中的Location模块,LLDebugTool提供了一个UI来随时修改这个模拟值,让你在调试时,随时模拟定位,LLDebugTool仍提供了很多其他的功能,如果你只需要模拟定位的功能,则只需要集成LLDebugTool/Location这个subspec就可以了。

后记

前言说过,定位除了CLLocationManager之外,MKMapView的showUserLocation也可以获取定位信息,那么如何解决这个问题呢? 你可以在LLDebugTool/Location中查看答案。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 关于iOS 11不能定位问题的解决方法

    问题: 最近将系统升级到iOS11之后,发现APP不提示否允许始终访问位置,iBeacon不起作用.我查看了一下手机隐私设置,如图: 原因: 因为苹果现在增加了一项新的隐私保护功能 Privacy - Location Always and When In Use Usage Description, 并且原有的 Privacy - Location Always Usage Description 被降级为 Privacy - Location When In Use Usage Descri

  • iOS中定位当前位置坐标及转换为火星坐标的方法

    定位和位置信息获取 定位和反查位置信息要加载两个动态库 CoreLocation.framework 和 MapKit.framework 一个获取坐标一个提供反查 复制代码 代码如下: // appDelgate.h #import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> #import <MapKit/MapKit.h>   @interface AppDelegate : UIResponder

  • IOS提醒用户重新授权打开定位功能

    iOS 8及以上版本最不为人知的一个特点是与应用设置的深层链接,用户可以根据APP的需要授权启用位置.通知.联系人.相机.日历以及健康等设置. 大多数应用程序仅仅是弹出一个包含操作指令的警示窗口,如"进入设置>隐私>位置>OUR_APP".例如,推特的应用程序有一个更为精致和友好的指示对话框,所以我就把它当做一个例子来使用(可惜大多数应用程序都会有一个非常糟糕的版本). 我现在以一个心情沮丧用户的身份写这个帖子,希望更多的iOS开发者能与用户设置建立直接的深层链接,尤

  • iOS 11 BUG的发现、定位和解决

    前言 在iOS 11发布之后,出现了一系列适配相关的问题,UIScrollView在pagingEnabled=YES时滑动手势不灵敏,UITableView的滑动删除功能变动,UIImagePickerViewController的取消按钮点击区域变小等,本文介绍其中一个UIAlertView问题,分享其发现.定位和解决. 正文 1.问题产生 问题的最初,是iOS 11正式版发布后不久,测试的同学提了一个iOS 11相关的BUG,表现是:在直播间内发送聊天信息,如果被禁言,会弹出"被禁言&qu

  • iOS开发系列--地图与定位源代码详解

    概览 现在很多社交.电商.团购应用都引入了地图和定位功能,似乎地图功能不再是地图应用和导航应用所特有的.的确,有了地图和定位功能确实让我们的生活更加丰富多彩,极大的改变了我们的生活方式.例如你到了一个陌生的地方想要查找附近的酒店.超市等就可以打开软件搜索周边;类似的,还有很多团购软件可以根据你所在的位置自动为你推荐某些商品.总之,目前地图和定位功能已经大量引入到应用开发中.今天就和大家一起看一下iOS如何进行地图和定位开发. 定位 地图 定位 要实现地图.导航功能,往往需要先熟悉定位功能,在iO

  • iOS11中的定位授权的解决方法

    本文介绍了iOS11中的定位授权的解决方法,分享给大家,具体如下: 前台定位权限 1,增加NSLocationWhenInUseUsageDescription. 2,创建CLLocationManager对象,在使用定位服务前调用requestWhenInUseAuthorization(). 3,通过func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorization

  • IOS 城市定位详解及简单实例

    IOS 城市定位 前言: 获取经纬度并且转换成城市 iOS8定位失败解决 获取中文城市 1.建立简单的项目, 导入CoreLoation.framework: 2.在Info.plist中加上NSLocationAlwaysUsageDescription值为AlwaysLocation: 3.使用CLLocationManager对象进行定位: _locationManger = [[CLLocationManager alloc] init]; _locationManger.delegat

  • 讲解iOS开发中基本的定位功能实现

    一.简单说明 1.CLLocationManager   CLLocationManager的常用操作和属性   开始用户定位- (void)startUpdatingLocation;   停止用户定位- (void) stopUpdatingLocation;   说明:当调用了startUpdatingLocation方法后,就开始不断地定位用户的位置,中途会频繁地调用代理的下面方法 复制代码 代码如下: - (void)locationManager:(CLLocationManager

  • IOS入门笔记之地理位置定位系统

    前言:关于地理位置及定位系统,在iOS开发中也比较常见,比如美团外面的餐饮店铺的搜索,它首先需要用户当前手机的位置,然后在这个位置附近搜索相关的餐饮店铺的位置,并提供相关的餐饮信息,再比如最常见的就是地图导航,地图导航更需要定位服务,然后根据用户的目的地选出一条路线.其实,作为手机用户这么长时间,或多或少会发现在有些app应用首次在你的手机安装成功后,首次启动可能就会提示"是否同意XXx(比如百度浏览器)获取当前位置"等这样一类的信息.可见地理位置及定位系统是企业app开发必不可少的技

  • iOS实现模拟定位功能的示例代码

    前言 App中越来越多的功能依赖用户实际的位置,例如基于用户位置提供推荐数据.基于定位判断某些功能是否可用,但是在开发调试中XCode却没有提供自定义的模拟定位的功能,所以本文主要的目的是现实一个可以在开发调试过程中随时模拟定位的功能. 思路 我们在iOS的app开发中通常采用的是CLLocationManager来获取用户当前的位置,当然也可以采用MKMapView的showUserLocation来获取用户的位置,所以我们分别针对这两种情况分析. CLLocationManager 采用CL

  • Flutter实现仿微信分享功能的示例代码

    目录 1.首先去pub官网 2 在微信开放平台注册开发者账号以及创建你的应用程序 3 在分享页面 3.1 初始化 3.2 检测微信是否安装 3.3 分享微信消息 总结 本文设计到的知识点有 主要问题 Flutter 用来快速开发 Android iOS平台应用,在Flutter 中,通过 fluwx或者fluwx_no_pay 插件来实现微信分享功能 主要还是看自己的需求,本示例我将按照没有支付的实现.至于为什么,主要是ios打包提审比较麻烦. 那么接下来就看一下如何实现吧, 1.首先去pub官

  • VUE饿了么树形控件添加增删改功能的示例代码

    本文介绍了VUE饿了么树形控件添加增删改功能的示例代码,分享给大家,具体如下: element-ui树形控件:地址 在原文档中有个案例是有新增和删除功能,但是后来发现其修改的数据并不能直接影响到树形数据,所以采用了 render-content 的API重新写了个组件. 写个开发的步骤,所以文章比较长emmm 大致效果如图: 1.省市API 在网上复制了个省市的list,有两个属性是新增的 isEdit :控制编辑状态 maxexpandId :为现下id的最大值 export default{

  • Android仿微信发送语音消息的功能及示例代码

    微信的发送语音是有一个向上取消的,我们使用onTouchListener来监听手势,然后做出相应的操作就行了. 直接上代码: //语音操作对象 private MediaPlayer mPlayer = null; private MediaRecorder mRecorder = null; //语音文件保存路径 private String FileName = null; FileName = Environment.getExternalStorageDirectory().getAbs

  • php 实现收藏功能的示例代码

    整理文档,搜刮出一个php 实现收藏功能的示例代码,稍微整理精简一下做下分享. HTML: <a class = "x" id="{$photo.id}" uid="{$Think.session.uid}" status = "{$collect_pic}" href = "javascript:void(0);"> <if condition = "$collect_num

  • React 实现拖拽功能的示例代码

    本文介绍了React 实现拖拽功能的示例代码,分享给大家,具体如下: 实现效果: 因为工作中会用到 JIRA 所以想实现一下相似的功能,顺便学习一下 H5 的拖拽.不支持拖拽改变顺序,感觉有点麻烦,而且没必要.感觉相关的博文好少的,大部分都是直接上代码,没有解释. 图片默认可以拖动,其他元素的拖动效果同图片.正常的 div 是不能被拖动的,鼠标点击选择后移动没有效果,需要加  draggable="true" 使得元素可以被拖动. 拖拽相关的几个事件,有被拖动元素的事件,也有拖动进入的

  • MyBatis XML方式的基本用法之多表查询功能的示例代码

    1. 多表查询 在之前,我们示例的2个查询都是单表查询,但实际的业务场景肯定是需要多表查询的,比如现在有个需求: 查询某个用户拥有的所有角色.这个需求要涉及到sys_user,sys_user_role,sys_role三张表,如何实现呢? 首先,在SysUserMapper接口中定义如下方法. /** * 根据用户id获取角色信息 * * @param userId * @return */ List<SysRole> selectRolesByUserId(Long userId); 然后

  • swift4 使用DrawerController实现侧滑菜单功能的示例代码

    本文介绍了swift4 使用DrawerController实现侧滑功能的示例代码,分享给大家,具体如下: 直接上图 安装 类库开源地址:https://github.com/sascha/DrawerController 可惜的是,它已经不维护了,很好用的一个侧滑实现 pod 'DrawerController' 新建侧滑视图 import UIKit // 这个类就是一个 UIViewController 可以在里面写任何你想写的东西 class LeftViewController: UI

  • Qt实现保存、浏览、预览、打印功能的示例代码

    Qt提供了以文本.图片.HTML等方式来实现对文档的操作,主要用到了QPrinter类和QPainter类,用到了QFileDialog文件窗口.QPrintPreviewDialog预览窗口类和QPrintDialog打印窗口类,Qt5也提供了QPdfWriter类来实现对pdf的操作,这里并不包括打开pdf文件,Qt没有提供任何方法来直接像文件浏览器一样打开pdf文件,可以用第三方库来实现. 这里采用了图片的方式来实现保存.预览和打印,其实 三个功能基本上一样. 1.保存PDF (1)保存某

  • koa2实现登录注册功能的示例代码

    本文介绍了koa2实现登录注册功能的示例代码,分享给大家,具体如下: 这个主要结合前几天的内容,做个实际案例的效果 版本: 项目结构: 前几天,我们把注册和登录的页面demo实现了,今天我们主要实现这么几个内容 注册新用户 判断该邮箱是否注册过 登录判断是否注册过 登录时的密码的正确 本文代码地址:https://github.com/xiaqijian/koa2-lessons/tree/master/lesson6 明天,我们将利用session实现登录状态判断 今天的这篇是在之前的代码基础

随机推荐