637 lines
26 KiB
Swift
637 lines
26 KiB
Swift
//
|
|
// LocationPickerViewController.swift
|
|
// QuickLocation
|
|
//
|
|
|
|
import UIKit
|
|
import CoreLocation
|
|
import IQKeyboardManagerSwift
|
|
import RxSwift
|
|
import RxCocoa
|
|
import SwiftyUserDefaults
|
|
#if !targetEnvironment(simulator)
|
|
import AMapNaviKit
|
|
import AMapSearchKit
|
|
#endif
|
|
|
|
struct PickedLocation {
|
|
let name: String
|
|
let address: String
|
|
let coordinate: CLLocationCoordinate2D
|
|
var province: String
|
|
var city: String
|
|
var district: String
|
|
var street: String
|
|
var country: String
|
|
var formatted_address: String
|
|
|
|
init(name: String,
|
|
address: String,
|
|
coordinate: CLLocationCoordinate2D,
|
|
province: String = "",
|
|
city: String = "",
|
|
district: String = "",
|
|
street: String = "",
|
|
country: String = "",
|
|
formatted_address: String = "") {
|
|
self.name = name
|
|
self.address = address
|
|
self.coordinate = coordinate
|
|
self.province = province
|
|
self.city = city
|
|
self.district = district
|
|
self.street = street
|
|
self.country = country
|
|
self.formatted_address = formatted_address
|
|
}
|
|
}
|
|
|
|
final class LocationSuggestionCell: UITableViewCell {
|
|
private let nameLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = .systemFont(ofSize: 16, weight: .medium)
|
|
label.textColor = UIColor(hexStr: "#1E2F4A")
|
|
label.lineBreakMode = .byTruncatingTail
|
|
return label
|
|
}()
|
|
private let distanceLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = .systemFont(ofSize: 12, weight: .bold)
|
|
label.textColor = UIColor(hexStr: "#50BCEF")
|
|
label.textAlignment = .center
|
|
label.backgroundColor = UIColor(hexStr: "#E3F6FF")
|
|
label.layer.cornerRadius = 6
|
|
label.clipsToBounds = true
|
|
return label
|
|
}()
|
|
private let addressLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = .systemFont(ofSize: 14)
|
|
label.textColor = UIColor(hexStr: "#A2A2A2")
|
|
label.lineBreakMode = .byTruncatingTail
|
|
return label
|
|
}()
|
|
private let checkmarkView: UIImageView = {
|
|
let view = UIImageView(image: UIImage(systemName: "checkmark"))
|
|
view.tintColor = UIColor(hexStr: "#16B3FF")
|
|
view.contentMode = .scaleAspectFit
|
|
return view
|
|
}()
|
|
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
backgroundColor = .white
|
|
selectionStyle = .none
|
|
contentView.addSubview(nameLabel)
|
|
contentView.addSubview(distanceLabel)
|
|
contentView.addSubview(addressLabel)
|
|
contentView.addSubview(checkmarkView)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func configure(location: PickedLocation, distanceText: String?, isSelected: Bool) {
|
|
nameLabel.text = location.name.isEmpty ? "已选位置" : location.name
|
|
addressLabel.text = location.address.isEmpty ? "暂无详细地址" : location.address
|
|
distanceLabel.text = distanceText
|
|
distanceLabel.isHidden = distanceText == nil
|
|
checkmarkView.isHidden = !isSelected
|
|
setNeedsLayout()
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
let horizontalInset: CGFloat = 32
|
|
let checkWidth: CGFloat = 24
|
|
let rightInset: CGFloat = 20
|
|
let availableWidth = max(0, contentView.bounds.width - horizontalInset - checkWidth - rightInset)
|
|
let distanceText = distanceLabel.text ?? ""
|
|
let distanceWidth = distanceLabel.isHidden
|
|
? 0
|
|
: max(58, (distanceText as NSString).size(withAttributes: [.font: distanceLabel.font as Any]).width + 14)
|
|
let nameWidth = max(0, availableWidth - (distanceLabel.isHidden ? 0 : distanceWidth + 8))
|
|
|
|
nameLabel.frame = CGRect(x: horizontalInset, y: 11, width: nameWidth, height: 24)
|
|
distanceLabel.frame = CGRect(x: nameLabel.frame.maxX + 8, y: 13, width: distanceWidth, height: 20)
|
|
addressLabel.frame = CGRect(x: horizontalInset, y: 42, width: availableWidth, height: 20)
|
|
checkmarkView.frame = CGRect(x: contentView.bounds.width - rightInset - checkWidth, y: 26, width: checkWidth, height: checkWidth)
|
|
}
|
|
}
|
|
|
|
#if !targetEnvironment(simulator)
|
|
final class CurrentUserLocationAnnotation: NSObject, MAAnnotation {
|
|
var coordinate: CLLocationCoordinate2D
|
|
|
|
init(coordinate: CLLocationCoordinate2D) {
|
|
self.coordinate = coordinate
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
final class CurrentUserLocationAnnotationView: MAAnnotationView {
|
|
private let avatarContainer: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = .white
|
|
view.layer.cornerRadius = 14
|
|
view.layer.shadowColor = UIColor.black.cgColor
|
|
view.layer.shadowOffset = CGSize(width: 0, height: 2)
|
|
view.layer.shadowRadius = 5
|
|
view.layer.shadowOpacity = 0.16
|
|
return view
|
|
}()
|
|
private let avatarImageView: UIImageView = {
|
|
let view = UIImageView()
|
|
view.contentMode = .scaleAspectFill
|
|
view.clipsToBounds = true
|
|
view.layer.cornerRadius = 10
|
|
return view
|
|
}()
|
|
private let statusOuterView: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = .white
|
|
view.layer.cornerRadius = 8
|
|
view.layer.shadowColor = UIColor.black.cgColor
|
|
view.layer.shadowOffset = CGSize(width: 0, height: 1)
|
|
view.layer.shadowRadius = 2
|
|
view.layer.shadowOpacity = 0.16
|
|
return view
|
|
}()
|
|
private let statusInnerView: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = UIColor(hexStr: "#56DC42")
|
|
view.layer.cornerRadius = 5
|
|
return view
|
|
}()
|
|
|
|
override init!(annotation: MAAnnotation!, reuseIdentifier: String!) {
|
|
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
|
|
backgroundColor = .clear
|
|
canShowCallout = false
|
|
isUserInteractionEnabled = false
|
|
bounds = CGRect(x: 0, y: 0, width: 56, height: 72)
|
|
centerOffset = CGPoint(x: 0, y: -29)
|
|
|
|
addSubview(avatarContainer)
|
|
avatarContainer.addSubview(avatarImageView)
|
|
addSubview(statusOuterView)
|
|
statusOuterView.addSubview(statusInnerView)
|
|
|
|
avatarContainer.frame = CGRect(x: 0, y: 0, width: 56, height: 56)
|
|
avatarImageView.frame = avatarContainer.bounds.insetBy(dx: 4, dy: 4)
|
|
statusOuterView.frame = CGRect(x: 20, y: 57, width: 16, height: 16)
|
|
statusInnerView.frame = statusOuterView.bounds.insetBy(dx: 3, dy: 3)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func configure() {
|
|
avatarImageView.setHeadPic(AppContextManager.shared.head_pic)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
final class LocationPickerViewController: BaseViewController {
|
|
override var isNavigationBarHidden: Bool { true }
|
|
|
|
var initialLocation: PickedLocation?
|
|
var onPickedLocation: ((PickedLocation) -> Void)?
|
|
|
|
private let collapsedResultPanelHeight: CGFloat = 290
|
|
private var rootView: LocationPickerView!
|
|
private var currentCoordinate: CLLocationCoordinate2D?
|
|
private var selectedLocation: PickedLocation?
|
|
private var suggestions: [PickedLocation] = []
|
|
private var searchWorkItem: DispatchWorkItem?
|
|
private var isResultPanelExpanded = false
|
|
private var keyboardManagerWasEnabled = true
|
|
private var keyboardManagerResignOnTouchOutside = true
|
|
|
|
#if !targetEnvironment(simulator)
|
|
private let searchAPI = AMapSearchAPI()
|
|
private var currentLocationAnnotation: CurrentUserLocationAnnotation?
|
|
#endif
|
|
|
|
override func loadView() {
|
|
rootView = LocationPickerView(frame: UIScreen.main.bounds)
|
|
view = rootView
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
rootView.resultTableView.dataSource = self
|
|
rootView.resultTableView.delegate = self
|
|
rootView.backBtn.addTarget(self, action: #selector(handleBack), for: .touchUpInside)
|
|
rootView.confirmBtn.addTarget(self, action: #selector(confirm), for: .touchUpInside)
|
|
rootView.recenterBtn.addTarget(self, action: #selector(recenterToCurrentLocation), for: .touchUpInside)
|
|
rootView.searchField.addTarget(self, action: #selector(searchChanged), for: .editingChanged)
|
|
rootView.searchField.addTarget(self, action: #selector(search), for: .editingDidEndOnExit)
|
|
setupKeyboard()
|
|
setupMap()
|
|
}
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
keyboardManagerWasEnabled = IQKeyboardManager.shared.isEnabled
|
|
keyboardManagerResignOnTouchOutside = IQKeyboardManager.shared.resignOnTouchOutside
|
|
IQKeyboardManager.shared.isEnabled = false
|
|
IQKeyboardManager.shared.resignOnTouchOutside = false
|
|
view.transform = .identity
|
|
}
|
|
|
|
override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
IQKeyboardManager.shared.isEnabled = keyboardManagerWasEnabled
|
|
IQKeyboardManager.shared.resignOnTouchOutside = keyboardManagerResignOnTouchOutside
|
|
}
|
|
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
super.viewDidDisappear(animated)
|
|
if isMovingFromParent || isBeingDismissed {
|
|
#if !targetEnvironment(simulator)
|
|
rootView.cleanupMap()
|
|
#endif
|
|
}
|
|
}
|
|
|
|
@objc private func handleBack() {
|
|
close()
|
|
}
|
|
|
|
@objc private func confirm() {
|
|
guard let selectedLocation,
|
|
CLLocationCoordinate2DIsValid(selectedLocation.coordinate) else {
|
|
DLToast.showError(text: "请选择位置")
|
|
return
|
|
}
|
|
onPickedLocation?(selectedLocation)
|
|
close()
|
|
}
|
|
|
|
private func close() {
|
|
view.endEditing(true)
|
|
if presentingViewController != nil {
|
|
dismiss(animated: true)
|
|
} else {
|
|
navigationController?.popViewController(animated: true)
|
|
}
|
|
}
|
|
|
|
@objc private func searchChanged() {
|
|
searchWorkItem?.cancel()
|
|
let workItem = DispatchWorkItem { [weak self] in self?.search() }
|
|
searchWorkItem = workItem
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35, execute: workItem)
|
|
}
|
|
|
|
@objc private func search() {
|
|
let keyword = rootView.searchField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
guard !keyword.isEmpty else {
|
|
loadNearbyPOIs(around: selectedLocation?.coordinate ?? currentCoordinate)
|
|
return
|
|
}
|
|
#if !targetEnvironment(simulator)
|
|
let request = AMapPOIKeywordsSearchRequest()
|
|
request.keywords = keyword
|
|
request.city = ""
|
|
request.offset = 20
|
|
if let coordinate = selectedLocation?.coordinate ?? currentCoordinate {
|
|
request.location = AMapGeoPoint.location(withLatitude: CGFloat(coordinate.latitude),
|
|
longitude: CGFloat(coordinate.longitude))
|
|
}
|
|
searchAPI?.aMapPOIKeywordsSearch(request)
|
|
#else
|
|
rootView.emptyLabel.isHidden = false
|
|
rootView.emptyLabel.text = "模拟器无法搜索地图地点"
|
|
#endif
|
|
}
|
|
|
|
private func setupKeyboard() {
|
|
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
|
|
.observe(on: MainScheduler.instance)
|
|
.subscribe(onNext: { [weak self] notification in
|
|
guard let self,
|
|
self.isViewLoaded,
|
|
self.view.window != nil,
|
|
self.rootView.searchField.isFirstResponder else { return }
|
|
let duration = (notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double) ?? 0.25
|
|
self.setResultPanelExpanded(true, duration: duration)
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func setResultPanelExpanded(_ expanded: Bool, duration: TimeInterval = 0.25) {
|
|
guard isResultPanelExpanded != expanded else { return }
|
|
isResultPanelExpanded = expanded
|
|
let height = expanded ? view.bounds.height * 2 / 3 : collapsedResultPanelHeight
|
|
rootView.setResultPanelHeight(height, duration: duration)
|
|
}
|
|
|
|
private func collapseResultPanel() {
|
|
setResultPanelExpanded(false)
|
|
rootView.searchField.resignFirstResponder()
|
|
}
|
|
|
|
@objc private func recenterToCurrentLocation() {
|
|
guard let currentCoordinate else {
|
|
DLToast.showError(text: "暂无当前位置")
|
|
return
|
|
}
|
|
#if !targetEnvironment(simulator)
|
|
rootView.mapView.setCenter(currentCoordinate, animated: true)
|
|
#endif
|
|
selectMapCenterLocation(currentCoordinate, placeholderName: "当前位置")
|
|
}
|
|
|
|
private func setupMap() {
|
|
if let latitude = Defaults[\.currentLatitude], let longitude = Defaults[\.currentLongitude] {
|
|
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
|
|
if CLLocationCoordinate2DIsValid(coordinate) {
|
|
currentCoordinate = coordinate
|
|
}
|
|
}
|
|
|
|
let validInitialLocation = initialLocation.flatMap { location in
|
|
CLLocationCoordinate2DIsValid(location.coordinate) ? location : nil
|
|
}
|
|
|
|
#if !targetEnvironment(simulator)
|
|
searchAPI?.delegate = self
|
|
rootView.mapView.delegate = self
|
|
rootView.mapView.showsUserLocation = false
|
|
let coordinate = validInitialLocation?.coordinate
|
|
?? currentCoordinate
|
|
?? CLLocationCoordinate2D(latitude: 39.9042, longitude: 116.4074)
|
|
rootView.mapView.setCenter(coordinate, animated: false)
|
|
rootView.mapView.setZoomLevel(15, animated: false)
|
|
if let currentCoordinate {
|
|
addCurrentLocationAnnotation(at: currentCoordinate)
|
|
}
|
|
if let validInitialLocation {
|
|
select(validInitialLocation, recenterMap: false)
|
|
requestReverseGeocode(for: validInitialLocation.coordinate)
|
|
loadNearbyPOIs(around: validInitialLocation.coordinate)
|
|
} else if let currentCoordinate {
|
|
selectMapCenterLocation(currentCoordinate, placeholderName: "当前位置")
|
|
}
|
|
#else
|
|
if let validInitialLocation {
|
|
select(validInitialLocation, recenterMap: false)
|
|
}
|
|
rootView.emptyLabel.isHidden = false
|
|
rootView.emptyLabel.text = "请在真机上选择地图位置"
|
|
#endif
|
|
}
|
|
|
|
private func loadNearbyPOIs(around coordinate: CLLocationCoordinate2D?) {
|
|
#if !targetEnvironment(simulator)
|
|
guard let coordinate, CLLocationCoordinate2DIsValid(coordinate) else {
|
|
rootView.emptyLabel.isHidden = false
|
|
rootView.emptyLabel.text = "暂无当前位置,可直接在地图上选择"
|
|
return
|
|
}
|
|
let request = AMapPOIAroundSearchRequest()
|
|
request.location = AMapGeoPoint.location(withLatitude: CGFloat(coordinate.latitude),
|
|
longitude: CGFloat(coordinate.longitude))
|
|
request.radius = 1500
|
|
request.offset = 20
|
|
searchAPI?.aMapPOIAroundSearch(request)
|
|
#endif
|
|
}
|
|
|
|
private func updateSuggestions(_ values: [PickedLocation]) {
|
|
suggestions = values
|
|
keepSelectedLocationInSuggestions()
|
|
rootView.resultTableView.reloadData()
|
|
rootView.emptyLabel.isHidden = !suggestions.isEmpty
|
|
}
|
|
|
|
private func select(_ location: PickedLocation, recenterMap: Bool) {
|
|
guard CLLocationCoordinate2DIsValid(location.coordinate) else { return }
|
|
selectedLocation = location
|
|
keepSelectedLocationInSuggestions()
|
|
rootView.confirmBtn.isEnabled = true
|
|
rootView.emptyLabel.isHidden = true
|
|
rootView.resultTableView.reloadData()
|
|
#if !targetEnvironment(simulator)
|
|
if recenterMap {
|
|
rootView.mapView.setCenter(location.coordinate, animated: true)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
private func selectMapCenterLocation(_ coordinate: CLLocationCoordinate2D,
|
|
placeholderName: String = "正在获取地点...") {
|
|
guard CLLocationCoordinate2DIsValid(coordinate) else { return }
|
|
select(PickedLocation(name: placeholderName,
|
|
address: "正在获取地址...",
|
|
coordinate: coordinate),
|
|
recenterMap: false)
|
|
#if !targetEnvironment(simulator)
|
|
requestReverseGeocode(for: coordinate)
|
|
loadNearbyPOIs(around: coordinate)
|
|
#endif
|
|
}
|
|
|
|
private func keepSelectedLocationInSuggestions() {
|
|
guard let selectedLocation else { return }
|
|
if let index = suggestions.firstIndex(where: { coordinatesMatch($0.coordinate, selectedLocation.coordinate) }) {
|
|
suggestions[index] = selectedLocation
|
|
} else {
|
|
suggestions.insert(selectedLocation, at: 0)
|
|
}
|
|
}
|
|
|
|
private func coordinatesMatch(_ lhs: CLLocationCoordinate2D, _ rhs: CLLocationCoordinate2D) -> Bool {
|
|
abs(lhs.latitude - rhs.latitude) < 0.000_001 && abs(lhs.longitude - rhs.longitude) < 0.000_001
|
|
}
|
|
|
|
private func distanceText(to coordinate: CLLocationCoordinate2D) -> String? {
|
|
guard let selectedLocation else { return nil }
|
|
let distance = CLLocation(latitude: selectedLocation.coordinate.latitude,
|
|
longitude: selectedLocation.coordinate.longitude)
|
|
.distance(from: CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude))
|
|
if distance < 100 {
|
|
return "100m内"
|
|
}
|
|
if distance < 1000 {
|
|
return "\(Int(distance.rounded()))m"
|
|
}
|
|
return String(format: "%.1fkm", distance / 1000)
|
|
}
|
|
|
|
#if !targetEnvironment(simulator)
|
|
private func requestReverseGeocode(for coordinate: CLLocationCoordinate2D) {
|
|
let request = AMapReGeocodeSearchRequest()
|
|
request.location = AMapGeoPoint.location(withLatitude: CGFloat(coordinate.latitude),
|
|
longitude: CGFloat(coordinate.longitude))
|
|
request.requireExtension = true
|
|
searchAPI?.aMapReGoecodeSearch(request)
|
|
}
|
|
|
|
private func addCurrentLocationAnnotation(at coordinate: CLLocationCoordinate2D) {
|
|
if let currentLocationAnnotation {
|
|
currentLocationAnnotation.coordinate = coordinate
|
|
return
|
|
}
|
|
let annotation = CurrentUserLocationAnnotation(coordinate: coordinate)
|
|
currentLocationAnnotation = annotation
|
|
rootView.mapView.addAnnotation(annotation)
|
|
}
|
|
|
|
private func location(from poi: AMapPOI) -> PickedLocation? {
|
|
guard let point = poi.location else { return nil }
|
|
let coordinate = CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude)
|
|
guard CLLocationCoordinate2DIsValid(coordinate) else { return nil }
|
|
return PickedLocation(name: poi.name ?? "",
|
|
address: poi.address ?? poi.district ?? "",
|
|
coordinate: coordinate,
|
|
province: poi.province ?? "",
|
|
city: poi.city ?? "",
|
|
district: poi.district ?? "",
|
|
formatted_address: poi.address ?? "")
|
|
}
|
|
#endif
|
|
}
|
|
|
|
extension LocationPickerViewController: UITableViewDataSource, UITableViewDelegate {
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
suggestions.count
|
|
}
|
|
|
|
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
|
guard scrollView === rootView.resultTableView else { return }
|
|
rootView.searchField.resignFirstResponder()
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: LocationPickerView.cellIdentifier,
|
|
for: indexPath) as? LocationSuggestionCell else {
|
|
return UITableViewCell()
|
|
}
|
|
let location = suggestions[indexPath.row]
|
|
let isSelected = selectedLocation.map { coordinatesMatch($0.coordinate, location.coordinate) } ?? false
|
|
cell.configure(location: location,
|
|
distanceText: distanceText(to: location.coordinate),
|
|
isSelected: isSelected)
|
|
return cell
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
tableView.deselectRow(at: indexPath, animated: true)
|
|
collapseResultPanel()
|
|
let location = suggestions[indexPath.row]
|
|
select(location, recenterMap: true)
|
|
rootView.searchField.text = location.name
|
|
#if !targetEnvironment(simulator)
|
|
requestReverseGeocode(for: location.coordinate)
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if !targetEnvironment(simulator)
|
|
extension LocationPickerViewController: AMapSearchDelegate, MAMapViewDelegate {
|
|
func onPOISearchDone(_ request: AMapPOISearchBaseRequest!, response: AMapPOISearchResponse!) {
|
|
if let aroundRequest = request as? AMapPOIAroundSearchRequest,
|
|
let point = aroundRequest.location {
|
|
let coordinate = CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude)
|
|
guard let selectedLocation, coordinatesMatch(selectedLocation.coordinate, coordinate) else { return }
|
|
} else if let keywordRequest = request as? AMapPOIKeywordsSearchRequest,
|
|
let point = keywordRequest.location {
|
|
let coordinate = CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude)
|
|
guard let selectedLocation, coordinatesMatch(selectedLocation.coordinate, coordinate) else { return }
|
|
}
|
|
updateSuggestions(response.pois.compactMap { self.location(from: $0) })
|
|
}
|
|
|
|
func onReGeocodeSearchDone(_ request: AMapReGeocodeSearchRequest!, response: AMapReGeocodeSearchResponse!) {
|
|
guard let point = request.location else { return }
|
|
let coordinate = CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude)
|
|
guard let selectedLocation, coordinatesMatch(selectedLocation.coordinate, coordinate) else { return }
|
|
|
|
let reGeocode = response.regeocode
|
|
let poi = reGeocode?.pois?.first
|
|
let component = reGeocode?.addressComponent
|
|
let formattedAddress = reGeocode?.formattedAddress ?? ""
|
|
let hasResolvedName = !selectedLocation.name.isEmpty
|
|
&& selectedLocation.name != "当前位置"
|
|
&& selectedLocation.name != "正在获取地点..."
|
|
let hasResolvedAddress = !selectedLocation.address.isEmpty
|
|
&& selectedLocation.address != "正在获取地址..."
|
|
let name = hasResolvedName
|
|
? selectedLocation.name
|
|
: (poi?.name ?? (formattedAddress.isEmpty ? "已选位置" : formattedAddress))
|
|
let address = hasResolvedAddress
|
|
? selectedLocation.address
|
|
: (poi?.address ?? formattedAddress)
|
|
|
|
select(PickedLocation(name: name,
|
|
address: address,
|
|
coordinate: coordinate,
|
|
province: component?.province ?? poi?.province ?? selectedLocation.province,
|
|
city: component?.city ?? poi?.city ?? selectedLocation.city,
|
|
district: component?.district ?? poi?.district ?? selectedLocation.district,
|
|
street: component?.streetNumber?.street ?? selectedLocation.street,
|
|
country: component?.country ?? selectedLocation.country,
|
|
formatted_address: formattedAddress.isEmpty
|
|
? (selectedLocation.formatted_address.isEmpty ? address : selectedLocation.formatted_address)
|
|
: formattedAddress),
|
|
recenterMap: false)
|
|
}
|
|
|
|
func aMapSearchRequest(_ request: Any!, didFailWithError error: Error!) {
|
|
if let reGeocodeRequest = request as? AMapReGeocodeSearchRequest,
|
|
let point = reGeocodeRequest.location {
|
|
let coordinate = CLLocationCoordinate2D(latitude: point.latitude, longitude: point.longitude)
|
|
if let selectedLocation, coordinatesMatch(selectedLocation.coordinate, coordinate) {
|
|
let name = selectedLocation.name == "正在获取地点..." ? "已选位置" : selectedLocation.name
|
|
let address = selectedLocation.address == "正在获取地址..." ? "" : selectedLocation.address
|
|
select(PickedLocation(name: name,
|
|
address: address,
|
|
coordinate: coordinate,
|
|
province: selectedLocation.province,
|
|
city: selectedLocation.city,
|
|
district: selectedLocation.district,
|
|
street: selectedLocation.street,
|
|
country: selectedLocation.country,
|
|
formatted_address: selectedLocation.formatted_address),
|
|
recenterMap: false)
|
|
}
|
|
return
|
|
}
|
|
rootView.emptyLabel.isHidden = false
|
|
rootView.emptyLabel.text = "地点加载失败,请重试或在地图上选择"
|
|
}
|
|
|
|
func mapView(_ mapView: MAMapView!, regionDidChangeAnimated animated: Bool, wasUserAction: Bool) {
|
|
guard wasUserAction else { return }
|
|
collapseResultPanel()
|
|
let center = CGPoint(x: mapView.bounds.midX, y: mapView.bounds.midY)
|
|
selectMapCenterLocation(mapView.convert(center, toCoordinateFrom: mapView))
|
|
}
|
|
|
|
func mapView(_ mapView: MAMapView!, mapWillMoveByUser wasUserAction: Bool) {
|
|
guard wasUserAction else { return }
|
|
collapseResultPanel()
|
|
}
|
|
|
|
func mapView(_ mapView: MAMapView!, didSingleTappedAt coordinate: CLLocationCoordinate2D) {
|
|
collapseResultPanel()
|
|
}
|
|
|
|
func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -> MAAnnotationView! {
|
|
guard annotation is CurrentUserLocationAnnotation else { return nil }
|
|
let identifier = "CurrentUserLocation"
|
|
guard let view = (mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? CurrentUserLocationAnnotationView)
|
|
?? CurrentUserLocationAnnotationView(annotation: annotation, reuseIdentifier: identifier) else { return nil }
|
|
view.annotation = annotation
|
|
view.zIndex = 10
|
|
view.configure()
|
|
return view
|
|
}
|
|
}
|
|
#endif
|