AutoCat/AutoCat/Controllers/Location/LocationPickerController.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)
}
}