161 lines
5.8 KiB
Swift
161 lines
5.8 KiB
Swift
import Foundation
|
|
import MapKit
|
|
import Eureka
|
|
import RxSwift
|
|
import Intents
|
|
|
|
public struct Placemark: Equatable {
|
|
var latitude: Double
|
|
var longitude: Double
|
|
var address: String?
|
|
}
|
|
|
|
public class LocationPickerController : UIViewController, TypedRowControllerType, MKMapViewDelegate {
|
|
|
|
public var row: RowOf<Placemark>!
|
|
public var onDismissCallback: ((UIViewController) -> ())?
|
|
|
|
private let bag = DisposeBag()
|
|
private var geocodingDisposable: Disposable?
|
|
private var address: String?
|
|
|
|
lazy var mapView : MKMapView = { [unowned self] in
|
|
let v = MKMapView(frame: self.view.bounds)
|
|
v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
return v
|
|
}()
|
|
|
|
lazy var pinView: UIImageView = { [unowned self] in
|
|
let v = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
|
|
v.image = UIImage(named: "MapPin", in: Bundle(for: LocationPickerController.self), compatibleWith: nil)
|
|
v.image = v.image?.withRenderingMode(.alwaysTemplate)
|
|
v.tintColor = self.view.tintColor
|
|
v.backgroundColor = .clear
|
|
v.clipsToBounds = true
|
|
v.contentMode = .scaleAspectFit
|
|
v.isUserInteractionEnabled = false
|
|
return v
|
|
}()
|
|
|
|
let width: CGFloat = 10.0
|
|
let height: CGFloat = 5.0
|
|
|
|
lazy var ellipse: UIBezierPath = { [unowned self] in
|
|
let ellipse = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: self.width, height: self.height))
|
|
return ellipse
|
|
}()
|
|
|
|
|
|
lazy var ellipsisLayer: CAShapeLayer = { [unowned self] in
|
|
let layer = CAShapeLayer()
|
|
layer.bounds = CGRect(x: 0, y: 0, width: self.width, height: self.height)
|
|
layer.path = self.ellipse.cgPath
|
|
layer.fillColor = UIColor.gray.cgColor
|
|
layer.fillRule = .nonZero
|
|
layer.lineCap = .butt
|
|
layer.lineDashPattern = nil
|
|
layer.lineDashPhase = 0.0
|
|
layer.lineJoin = .miter
|
|
layer.lineWidth = 1.0
|
|
layer.miterLimit = 10.0
|
|
layer.strokeColor = UIColor.gray.cgColor
|
|
return layer
|
|
}()
|
|
|
|
|
|
required public init?(coder aDecoder: NSCoder) {
|
|
super.init(coder: aDecoder)
|
|
}
|
|
|
|
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
|
super.init(nibName: nil, bundle: nil)
|
|
}
|
|
|
|
convenience public init(_ callback: ((UIViewController) -> ())?){
|
|
self.init(nibName: nil, bundle: nil)
|
|
onDismissCallback = callback
|
|
}
|
|
|
|
public override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
view.addSubview(mapView)
|
|
|
|
mapView.delegate = self
|
|
mapView.addSubview(pinView)
|
|
mapView.layer.insertSublayer(ellipsisLayer, below: pinView.layer)
|
|
|
|
let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(LocationPickerController.tappedDone(_:)))
|
|
button.title = "Done"
|
|
navigationItem.rightBarButtonItem = button
|
|
|
|
if let value = row.value {
|
|
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: value.latitude, longitude: value.longitude), latitudinalMeters: 1000, longitudinalMeters: 1000)
|
|
mapView.setRegion(region, animated: true)
|
|
}
|
|
else{
|
|
mapView.showsUserLocation = true
|
|
}
|
|
updateTitle()
|
|
|
|
}
|
|
|
|
public override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
|
|
let center = mapView.convert(mapView.centerCoordinate, toPointTo: pinView)
|
|
pinView.center = CGPoint(x: center.x, y: center.y - (pinView.bounds.height/2))
|
|
ellipsisLayer.position = center
|
|
}
|
|
|
|
|
|
@objc func tappedDone(_ sender: UIBarButtonItem){
|
|
let target = mapView.convert(ellipsisLayer.position, toCoordinateFrom: mapView)
|
|
row.value = Placemark(latitude: target.latitude, longitude: target.longitude, address: self.address)
|
|
onDismissCallback?(self)
|
|
}
|
|
|
|
func updateTitle(){
|
|
let fmt = NumberFormatter()
|
|
fmt.maximumFractionDigits = 4
|
|
fmt.minimumFractionDigits = 4
|
|
let latitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.latitude))!
|
|
let longitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.longitude))!
|
|
title = "\(latitude), \(longitude)"
|
|
|
|
self.address = nil
|
|
self.geocodingDisposable?.dispose()
|
|
self.geocodingDisposable = LocationManager
|
|
.getAddressForLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
|
|
.observeOn(MainScheduler.instance)
|
|
.subscribe(onSuccess: { address in
|
|
self.title = address
|
|
self.address = address
|
|
})
|
|
}
|
|
|
|
public func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
|
|
ellipsisLayer.transform = CATransform3DMakeScale(0.5, 0.5, 1)
|
|
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
|
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y - 10)
|
|
})
|
|
}
|
|
|
|
public func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
|
|
ellipsisLayer.transform = CATransform3DIdentity
|
|
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
|
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y + 10)
|
|
})
|
|
updateTitle()
|
|
}
|
|
|
|
public func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
|
|
let region = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
|
|
mapView.setRegion(region, animated: true)
|
|
mapView.showsUserLocation = false
|
|
}
|
|
|
|
public func mapView(_ mapView: MKMapView, didFailToLocateUserWithError error: Error) {
|
|
print(error)
|
|
}
|
|
}
|