Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【iOS】RxSwift官方Example3--地理位置监听

【iOS】RxSwift官方Example3--地理位置监听

作者头像
MapleYe
发布于 2020-03-31 03:02:02
发布于 2020-03-31 03:02:02
1.2K00
代码可运行
举报
文章被收录于专栏:MapleYeMapleYe
运行总次数:0
代码可运行

前言

其实,这一篇的题目,我觉得应该是RxSwift对代理的封装,最后还是沿用官方Example的命名吧。

效果说明

图一
图一
图二
图二

图一是当App可以使用定位信息时,显示当前的经纬度。

图二是当App被禁止使用定位信息时,显示的提示信息

代码解释

比起上两个Example,这个Example复杂的多了。主要复杂在对Delegate的封装。

如何使用RxSwift对Delegate的封装稍后再说,先看看封装后的使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let service = GeolocationService.instance
        // 将是否允许使用定位的“Bool”绑定noGeolocationView.rx.isHidden
        service.authorized
        .drive(noGeolocationView.rx.isHidden)
        .addDisposableTo(disposeBag)
        // 将定位信息绑定在showLocationLabel.rx.coordinates
        service.location
        .drive(showLocationLabel.rx.coordinates)
        .addDisposableTo(disposeBag)

1、对UILabel的扩展

可以看到上面的例子,将CLLocationCoordinate2D的经纬度信息绑定在label上了。当想绑定的在视图信息越多,我们就需要对UILabel进行扩展。

扩展方法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
意思就是当Reactive的Base对象是UILabel时,增加一个类型为UIBindingObserver<Base, CLLocationCoordinate2D>的coordinates属性。
*/ 
private extension Reactive where Base: UILabel {
    var coordinates: UIBindingObserver<Base, CLLocationCoordinate2D> {
        return UIBindingObserver(UIElement: base, binding: { (label, location) in
            label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)"
        })
    }
}

在后面的block参数列表中,label的类型是泛型中的Base类型(例子是UILabel),location是泛型中的CLLocationCoordinate2D对象。因此,我们只要在block中对label和location对象做操作即可。

2、封装CLLocationManagerDelegate

2.1、创建CLLocationManagerDelegate的代理Proxy

为了创建Proxy对象,我们需要继承DelegateProxy,并且遵守DelegateProxyType协议,该协议必须实现以下两个方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// 返回object的代理对象
    class func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
        let locationManager: CLLocationManager = object as! CLLocationManager
        return locationManager.delegate
    }

    /// 设置objct代理对象
    class func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
        let locationManager: CLLocationManager = object as! CLLocationManager
        if let delegate = delegate {
            locationManager.delegate = (delegate as! CLLocationManagerDelegate)
        } else {
            locationManager.delegate = nil
        }
    }

2.2、创建PublishSubject对象

先来简单回顾一下概念:

subject的概念

Subject可以看做是一种代理和桥梁。它既是订阅者又是订阅源,这意味着它既可以订阅其他Observable对象,同时又可以对它的订阅者们发送事件。

PublishSubject的概念

当你订阅PublishSubject的时候,你只能接收到订阅他之后发生的事件

因此为了能够成为代理的代理,我们需要监听代理的事件,并且能够让外部进行监听,所以我们创建了以下两个publishSubject对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal lazy var didUpdateLocationsSubject = PublishSubject<[CLLocation]>()
    internal lazy var didFailWithErrorSubject = PublishSubject<Error>()

将代理事件通过subject传递出去,记得调用_forwardToDelagate?.method方法,因为我们这里只是做了一层监听中转

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        _forwardToDelegate?.locationManager(manager, didUpdateLocations: locations)
        didUpdateLocationsSubject.onNext(locations)
    }

    public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        _forwardToDelegate?.locationManager(manager, didFailWithError: error)
        didFailWithErrorSubject.onNext(error)
    }

代理和proxy之间的层级关系图:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
      +-------------------------------------------+
      |                                           |                           
      | UIView subclass (UIScrollView)            |                           
      |                                           |
      +-----------+-------------------------------+                           
                  |                                                           
                  | Delegate                                                  
                  |                                                           
                  |                                                           
      +-----------v-------------------------------+                           
      |                                           |                           
      | Delegate proxy : DelegateProxyType        +-----+---->  Observable<T1>
      |                , UIScrollViewDelegate     |          |
      +-----------+-------------------------------+     +---->  Observable<T2>
                  |                                     |                     
                  |                                     +---->  Observable<T3>
                  |                                     |                     
                  | forwards events                     |
                  | to custom delegate                  |
                  |                                     v                     
      +-----------v-------------------------------+                           
      |                                           |                           
      | Custom delegate (UIScrollViewDelegate)    |                           
      |                                           |
      +-------------------------------------------+   

2.3、对CLLocationManager做扩展

将想被监听的属性,通过刚才创建Proxy传递出来,于是我们只需要创建对应的Observable。

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
extension Reactive where Base: CLLocationManager {
    /**
     Reactive wrapper for `delegate`.
     
     For more information take a look at `DelegateProxyType` protocol documentation.
     */
    public var delegate: DelegateProxy {
        return RxCLLocationManagerDelegateProxy.proxyForObject(base)
    }
    
    // MARK: Responding to Authorization Changes
    
    /**
     Reactive wrapper for `delegate` message.
     */
    public var didChangeAuthorizationStatus: Observable<CLAuthorizationStatus> {
        return delegate.methodInvoked(#selector(CLLocationManagerDelegate.locationManager(_:didChangeAuthorization:)))
            .map { a in
                let number = try castOrThrow(NSNumber.self, a[1])
                return CLAuthorizationStatus( rawValue: Int32(number.intValue)) ?? .notDetermined
        }
    }
    
    // MARK: Responding to Location Events
    
    /**
     Reactive wrapper for `delegate` message.
     */
    public var didUpdateLocations: Observable<[CLLocation]> {
        return (delegate as! RxCLLocationManagerDelegateProxy).didUpdateLocationsSubject.asObservable()
    }
    
    /**
     Reactive wrapper for `delegate` message.
     */
    public var didFailWithError: Observable<Error> {
        return (delegate as! RxCLLocationManagerDelegateProxy).didFailWithErrorSubject.asObservable()
    }
}

这里值得一提的是调后delegate.methodInvoked,会返回Observable<[Any]>,其中数组装的就是传递给selector的参数,所以后面的map的block中,a[1]代表的就是CLAuthorizationStatus枚举类型。

此外,还定义了一个转类型的函数,转失败后,会发出Error

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fileprivate func castOrThrow<T>(_ resultType: T.Type, _ object: Any) throws -> T {
    guard let returnValue = object as? T else {
        throw RxCocoaError.castingError(object: object, targetType: resultType)
    }
    
    return returnValue
}

2.4、定义service层

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class GeolocationService {
static let instance = GeolocationService()
private (set) var authorized: Driver<Bool>
private (set) var location: Driver<CLLocationCoordinate2D>

private let locationManager = CLLocationManager()

private init() {
    
    locationManager.distanceFilter = kCLDistanceFilterNone
    locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    
    // deferred会为每一位订阅者observer创建一个新的可观察序列
    authorized = Observable.deferred { [weak locationManager] in
        let status = CLLocationManager.authorizationStatus()
        guard let locationManager = locationManager else {
            return Observable.just(status)
        }
        return locationManager
            .rx.didChangeAuthorizationStatus
            .startWith(status)
        }
        .asDriver(onErrorJustReturn: CLAuthorizationStatus.notDetermined)
        .map {
            switch $0 {
            case .authorizedAlways:
                return true
            default:
                return false
            }
    }
    
    location = locationManager.rx.didUpdateLocations
        .asDriver(onErrorJustReturn: [])
        .flatMap {
            return $0.last.map(Driver.just) ?? Driver.empty()
        }
        .map { $0.coordinate }
    locationManager.requestAlwaysAuthorization()
    locationManager.startUpdatingLocation()
}
}

这里的service层就是将之前扩展的LocationManager再次封装。值得一提的是,这里的authorized是使用deferred创建的。

deferred

deferred会等到有订阅者的时候再通过工厂方法创建Observable对象,每个订阅者订阅的对象都是内容相同而完全独立的序列。

因此,每次订阅authorized信息时,都会发送独立的序列,确保每次都会响应。

总结

该Example可以当作封装Delegate的介绍,因此可以总结一下封装流程如下:

  • 创建代理Proxy,需要继承继承DelegateProxy,并且遵守DelegateProxyType协议
  • 定义subject对象,即订阅者(订阅代理)又是订阅源(被外部订阅)

之后的什么扩展,service层就看大家的需要而定制了,但是以上的两步是必须的。最后,有不正确的地方,欢迎指正~

Demo地址

https://github.com/maple1994/RxSwfitTest

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
iOS区域监控(地理围栏)
区域监控,高德地图上叫地理围栏,两者都是同一个意思。此功能实现的是:首先创建一个区域(围栏),当用户设备进入或者离开此区域时,会有相应的代理方法响应,开发者可以做一些操作。并且最重要的一点是当开启了区域监控,即使用户杀死了APP还是可以监听到代理方法的响应,从而做一些操作。
用户6094182
2019/08/23
1.6K0
iOS区域监控(地理围栏)
iOS-世界那么大,CoreLocation带你去看看
一. 简介 在我们日常生活中时常用到地图和定位功能,来导航去你想去的地方或者寻找周边的景点,餐厅,电影院等等,在iOS开发中,要想加入这两大功能,必须基于两个框架进行开发,有了这两个框架,想去哪就去哪。 CoreLocation :用于地理定位,地理编码,区域监听等(着重功能实现) MapKit :用于地图展示,例如大头针,路线、覆盖层展示等(着重界面展示) 二. CoreLocation框架的基本使用 1. CoreLocation使用步骤 导入CoreLocation框架。 创建CLLocation
xx_Cc
2018/05/10
1.5K0
iOS-CoreLocation框架的定位和逆地址解析详解
一、权限问题 在iOS8以后,应用定位需要获取用户授权,我们可以请求的定位权限有两种: 1.仅在使用时定位requestWhenInUseAuthorization(应用在前台才能定位); 2.始
用户2215591
2018/06/29
1.4K0
iOS iOS 地图与定位开发系列教程
iPhone SDK提供了三个类来管理位置信息:CLLocation CLLocationManager 和 CLLHeading(不常用)。除了使用GPS来获取当前的位置信息外,iPhone也可以基于WiFi基站和无线发射塔来获得位置信息。GPS的精度最高,可以精确到米级别,但是也最耗电。
全栈程序员站长
2022/09/17
2.6K0
【IOS开发基础系列】地图开发专题
http://www.cnblogs.com/syxchina/archive/2012/10/14/2723522.html
江中散人_Jun
2023/10/16
5050
【IOS开发基础系列】地图开发专题
iOS地理围栏技术的应用
遇到一个需求,要求监测若干区域,设备进入这些区域则要上传数据,且可以后台监测,甚至app被杀死也要监测。发现oc的地理围栏技术完美匹配这个需求,任务做完了,把遇到的坑记录下来,也许能帮到你呢。 要做这个需求,我们需要把任务分成两大块,一块是支持后台监测且app被杀掉也要持续监测,另一块是如何进行区域监测。 而区域监测我们有3种方法完成: 1,oc自有的,利用CLLocationManager监测若干CLCircularRegion区域 2,高德地图旧版地理围栏,利用AMapLocationManager监测
王大锤
2018/05/17
2.2K0
iOS_系统自带地图圆形区域选择范围
5.聚集操作:删除原理的大头针,在新经纬度添加大头针,并将地图移动到新的经纬度(反地理编码获得位置信息)
mikimo
2022/07/20
2.4K0
iOS_系统自带地图圆形区域选择范围
iOS开发-用户定位获取-CoreLocation的实际应用-CLLocationManger获取定位权限-CLLocation详细使用方式
iOS提供了两个框架用来定位以及地图显示。CoreLocation框架包含的类可以帮助设备确定位置和航向以及使用基于位置的有效信息。MapKit框架未定位提供了户用页面的支持(地图显示),里面包含了地图视图、卫星地图视图以及2D、3D混合视图,并且能够让开发人员管理地图标注和地图覆盖层,前者 用于标注地点(常见的地图大头针),后者用来突出某区域或者路线等。
全栈程序员站长
2022/09/17
4.8K0
iOS开发-用户定位获取-CoreLocation的实际应用-CLLocationManger获取定位权限-CLLocation详细使用方式
IOS 定位CoreLocation代码
定位需要使用苹果官方的类库CoreLocation,通过GPS来确定位置信息 并且需要实现CLLocationManagerDelegate协议 1.首先添加类库CoreLocation
用户8671053
2021/10/29
6160
iOS原生定位和反编码
@property (nonatomic, strong) CLLocationManager *lcManager
ppppy
2022/11/15
4630
IOS定位服务的应用 原
在IOS8之后,IOS的定位服务做了优化,若要使用定位服务,必须先获取用户的授权。
珲少
2018/08/15
9420
IOS定位服务的应用
                                                                            原
IOS 定位CoreLocation
import CoreLocation 2 class ViewController:UIViewController,CLLocationManagerDelegate 3 var locationManager:CLLocationManager! 4 var label:UILabel! 5 override func viewDidLoad() { 6 super.viewDidLoad() 7 // Do any additional setup after loading the view, typically from a nib. 8 9 locationManager = CLLocationManager() 10 locationManager.delegate = self 11 locationManager.desiredAccuracy = kCLLocationAccuracyBest 12 locationManager.distanceFilter = 1000.0 13 14 label = UILabel(frame:CGRect(x:20, y:80, width: 280, height:100)) 15 label.numberOfLines = 2 16 label.backgroundColor = UIColor.brown 17 self.view.addSubview(label) 18 19 if CLLocationManager.authorizationStatus() == .notDetermined { 20 locationManager.requestAlwaysAuthorization() 21 } 22 } 23 func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { 24 switch status { 25 case .denied: 26 print(“用户拒绝您对地理设备使用的请求。”) 27 break; 28 default: 29 manager.startUpdatingLocation() 30 break; 31 } 32 } 33 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { 34 locationManager.stopUpdatingLocation() 35 36 let location:CLLocation = locations[0] 37 let latitude = location.coordinate.latitude 38 let longitude = location.coordinate.longitude 39 40 label.text = “经度:(longitude)\n 纬度:(latitude)” 41 }
用户5760343
2019/07/10
4550
iOS地图开发1-定位(swift)
关于ios中地图定位相关的开发可以分两块,一块为调用ios的定位获取GPS坐标以及坐标–>地址,地址–>坐标,另一块就是调用苹果对地图的封装,也可以调用高德或者百度地图的SDK,不过引用将近10M的库,但是功能上要比直接调用系统封装的要强大的多,所以我建议app里基本上只要求定位与地图上显示的,就直接用原生的封装,自定义较多的,比如设置定位点覆盖物的图标就引用第三方的
码客说
2019/10/22
1.6K0
iOS关于地图定位基础(二)[通俗易懂]
在前一篇文章 iOS关于地图定位基础(一) 中我们主要总结了 iOS 里面利用原生 CoreLocation 框架实现基本定位功能和一些注意点,侧重点主要是iOS8+之后的定位授权与授权状态的使用。接下来本篇文章主要是讲解如何利用 CoreLocation 框架实现地理定位、区域监听、地理编码的具体实现。(PS:下文涉及我自定义的指南针Demo请去我的GitHub仓库查看源码https://github.com/IMLoser/HWCompass,谢谢大家支持。)
全栈程序员站长
2022/09/17
1.1K0
iOS关于地图定位基础(二)[通俗易懂]
iOS学习——自动定位
  最近在项目中需要做自动定位功能,就是你在参加会议通过扫描二维码签到的时候自动定位并将你的定位信息在签到中上传,这样可以避免我们进行假签到。在这个功能中,主要用到的是系统自带的定位模块,首先我们是需要配置定位功能的参数,然后当我们定位成功时调用特定的方法进行相应操作就可以了,当然,在定位失败时我们也可以进行相应的操作,这些都有对应的一些回调方法,我们只需要重写对应的回调方法就可以实现对应的功能了。   首先,我们用到的系统自带的定位模块是: <CoreLocation/CoreLocation.h> ,定
mukekeheart
2018/03/26
9790
swift 定位封装一句话使用
git地址:https://github.com/RainManGO/LocationManager
星宇大前端
2019/01/15
1.4K0
iOS开发之定位
在iOS开发中,定位是很多App都需要使用的功能。本文主要对iOS中的定位知识点进行介绍。本文代码环境为:Xcode 10.1 + Swift 4.2。
YungFan
2019/03/21
1.6K0
iOS开发之定位
RxSwift - API
在某些情况,由于不同平台/实现方式,相同的操作符有多个别名,有时相同的操作命名也不一样,有些是因为历史的原因,但是还有一些是因为语言的预留关键字。
hrscy
2018/08/30
9020
CLLocation定位
typealias LocationClosure = ((_ sheng: String, _ shi: String, _ qu: String)->Void)
全栈程序员站长
2022/09/17
7050
解析SwiftUI布局细节(三)地图的基本操作
前面的几篇文章总结了怎样用 SwiftUI 搭建基本框架时候的一些注意点(和这篇文章在相同的分类里面,有需要了可以点进去看看),这篇文章要总结的东西是用地图数据处理结合来说的,通过这篇文章我们能总结到的点有下面几点:
Mr.RisingSun
2021/01/07
2.3K0
相关推荐
iOS区域监控(地理围栏)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验