227 lines
8.5 KiB
Swift
227 lines
8.5 KiB
Swift
//
|
|
// GroupScheduleVC.swift
|
|
// QuickLocation
|
|
//
|
|
// Created by 八条 on 2026/6/29.
|
|
//
|
|
|
|
import UIKit
|
|
import RxSwift
|
|
import RxCocoa
|
|
import ObjectMapper
|
|
import SwiftyUserDefaults
|
|
import AMapNaviKit
|
|
import AMapSearchKit
|
|
|
|
class GroupScheduleVC: BaseViewController {
|
|
|
|
fileprivate var rootView: GroupScheduleView!
|
|
|
|
override func loadView() {
|
|
rootView = GroupScheduleView(frame: UIScreen.main.bounds)
|
|
view = rootView
|
|
}
|
|
|
|
private let groupKey: String
|
|
private var scheduleList: [ScheduleModel] = []
|
|
private var selectedIndex: Int?
|
|
private let routeSearch = AMapSearchAPI()
|
|
private var routeOverlays: [MAPolyline] = []
|
|
private var pointAnnotations: [MAPointAnnotation] = []
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
rootView.tableView.dataSource = self
|
|
rootView.tableView.delegate = self
|
|
setupMap()
|
|
requestGroupScheduleList()
|
|
}
|
|
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
super.viewDidDisappear(animated)
|
|
if isMovingFromParent || isBeingDismissed {
|
|
rootView.cleanupMap()
|
|
}
|
|
}
|
|
|
|
// MARK: - API
|
|
private func requestGroupScheduleList() {
|
|
DLToast.showLoading()
|
|
ItineraryService.groupScheduleList(groupKey: groupKey).subscribe(onNext: { response in
|
|
DLToast.dismiss()
|
|
guard response.list.count > 0 else {
|
|
DLToast.show(text: "暂无行程数据") {
|
|
AppRouter.shared.popOrDismiss()
|
|
}
|
|
return
|
|
}
|
|
self.scheduleList = response.list
|
|
self.rootView.tableView.reloadData()
|
|
}).disposed(by: disposeBag)
|
|
}
|
|
|
|
// MARK: - Map
|
|
private func setupMap() {
|
|
rootView.mapView.delegate = self
|
|
routeSearch?.delegate = self
|
|
if let lat = Defaults[\.currentLatitude], let lon = Defaults[\.currentLongitude] {
|
|
let coord = CLLocationCoordinate2D(latitude: lat, longitude: lon)
|
|
if CLLocationCoordinate2DIsValid(coord) {
|
|
rootView.mapView.setCenter(coord, animated: false)
|
|
rootView.mapView.setZoomLevel(14, animated: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func showScheduleOnMap(_ model: ScheduleModel) {
|
|
// 清除旧标注和路线
|
|
for ann in pointAnnotations { rootView.mapView.removeAnnotation(ann) }
|
|
for ol in routeOverlays { rootView.mapView.remove(ol) }
|
|
pointAnnotations.removeAll()
|
|
routeOverlays.removeAll()
|
|
|
|
// 添加带序号的标注
|
|
let validPoints = model.points.filter {
|
|
guard let lat = $0.latitude, let lon = $0.longitude else { return false }
|
|
return abs(lat) > 0.0001 && abs(lon) > 0.0001
|
|
}
|
|
for (i, p) in validPoints.enumerated() {
|
|
let ann = MAPointAnnotation()
|
|
ann.coordinate = CLLocationCoordinate2D(latitude: p.latitude!, longitude: p.longitude!)
|
|
ann.title = "\(i + 1)"
|
|
rootView.mapView.addAnnotation(ann)
|
|
pointAnnotations.append(ann)
|
|
}
|
|
|
|
// 缩放至所有点位
|
|
if !pointAnnotations.isEmpty {
|
|
rootView.mapView.showAnnotations(pointAnnotations, animated: true)
|
|
}
|
|
|
|
// 驾车路线
|
|
guard validPoints.count >= 2 else { return }
|
|
let request = AMapDrivingRouteSearchRequest()
|
|
request.origin = AMapGeoPoint.location(withLatitude: CGFloat(validPoints[0].latitude!),
|
|
longitude: CGFloat(validPoints[0].longitude!))
|
|
request.destination = AMapGeoPoint.location(withLatitude: CGFloat(validPoints.last!.latitude!),
|
|
longitude: CGFloat(validPoints.last!.longitude!))
|
|
if validPoints.count > 2 {
|
|
var waypoints: [AMapGeoPoint] = []
|
|
for i in 1..<validPoints.count - 1 {
|
|
if let wp = AMapGeoPoint.location(withLatitude: CGFloat(validPoints[i].latitude ?? 0),
|
|
longitude: CGFloat(validPoints[i].longitude ?? 0)) {
|
|
waypoints.append(wp)
|
|
}
|
|
}
|
|
request.waypoints = waypoints
|
|
}
|
|
request.strategy = 0
|
|
routeSearch?.aMapDrivingRouteSearch(request)
|
|
}
|
|
|
|
private static func numberImage(_ num: Int) -> UIImage? {
|
|
let size = CGSize(width: 20, height: 20)
|
|
let rect = CGRect(origin: .zero, size: size)
|
|
UIGraphicsBeginImageContextWithOptions(size, false, 0)
|
|
guard let ctx = UIGraphicsGetCurrentContext() else { return nil }
|
|
ctx.setLineWidth(1)
|
|
ctx.setStrokeColor(UIColor.white.cgColor)
|
|
ctx.setFillColor(UIColor(hexStr: "#16B3FF").cgColor)
|
|
let path = UIBezierPath(ovalIn: rect)
|
|
path.fill()
|
|
path.stroke()
|
|
let text = "\(num)" as NSString
|
|
let attrs: [NSAttributedString.Key: Any] = [.font: UIFont.boldSystemFont(ofSize: 11), .foregroundColor: UIColor.white]
|
|
let strSize = text.size(withAttributes: attrs)
|
|
text.draw(at: CGPoint(x: (size.width - strSize.width) / 2, y: (size.height - strSize.height) / 2))
|
|
let img = UIGraphicsGetImageFromCurrentImageContext()
|
|
UIGraphicsEndImageContext()
|
|
return img
|
|
}
|
|
|
|
// MARK: - Init
|
|
init(groupKey: String) {
|
|
self.groupKey = groupKey
|
|
super.init(nibName: nil, bundle: nil)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
}
|
|
|
|
// MARK: - UITableViewDataSource & Delegate
|
|
extension GroupScheduleVC: UITableViewDataSource, UITableViewDelegate {
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
scheduleList.count
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cell: GroupScheduleCell = tableView.dequeueReusableCell(for: indexPath)
|
|
cell.configure(scheduleList[indexPath.row], isSelected: indexPath.row == selectedIndex)
|
|
return cell
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
selectedIndex = indexPath.row
|
|
tableView.reloadData()
|
|
showScheduleOnMap(scheduleList[indexPath.row])
|
|
rootView.dismissPanel()
|
|
}
|
|
}
|
|
|
|
// MARK: - MAMapViewDelegate
|
|
extension GroupScheduleVC: MAMapViewDelegate {
|
|
func mapView(_ mapView: MAMapView!, viewFor annotation: MAAnnotation!) -> MAAnnotationView! {
|
|
guard !(annotation is MAUserLocation), let pointAnn = annotation as? MAPointAnnotation else { return nil }
|
|
if let num = Int(pointAnn.title ?? "") {
|
|
let id = "SchedulePin"
|
|
var view = mapView.dequeueReusableAnnotationView(withIdentifier: id)
|
|
if view == nil { view = MAAnnotationView(annotation: annotation, reuseIdentifier: id) }
|
|
else { view?.annotation = annotation }
|
|
view?.image = Self.numberImage(num)
|
|
view?.centerOffset = CGPoint(x: 0, y: -15)
|
|
return view
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func mapView(_ mapView: MAMapView!, rendererFor overlay: MAOverlay!) -> MAOverlayRenderer! {
|
|
if let polyline = overlay as? MAPolyline {
|
|
let r = MAPolylineRenderer(polyline: polyline)
|
|
r?.strokeColor = UIColor(hexStr: "#16B3FF")
|
|
r?.lineWidth = 3
|
|
r?.lineDashType = kMALineDashTypeSquare
|
|
return r
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// MARK: - AMapSearchDelegate
|
|
extension GroupScheduleVC: AMapSearchDelegate {
|
|
func onRouteSearchDone(_ request: AMapRouteSearchBaseRequest!, response: AMapRouteSearchResponse!) {
|
|
guard let path = response.route?.paths?.first as? AMapPath else { return }
|
|
var coords: [CLLocationCoordinate2D] = []
|
|
for step in path.steps {
|
|
guard let polylineStr = step.polyline else { continue }
|
|
for point in polylineStr.components(separatedBy: ";") {
|
|
let latLon = point.components(separatedBy: ",")
|
|
if latLon.count == 2, let lon = Double(latLon[0]), let lat = Double(latLon[1]) {
|
|
coords.append(CLLocationCoordinate2D(latitude: lat, longitude: lon))
|
|
}
|
|
}
|
|
}
|
|
guard coords.count > 1 else { return }
|
|
var mutableCoords = coords
|
|
if let polyline = MAPolyline(coordinates: &mutableCoords, count: UInt(coords.count)) {
|
|
rootView.mapView.add(polyline)
|
|
routeOverlays.append(polyline)
|
|
}
|
|
}
|
|
|
|
func aMapSearchRequest(_ request: Any!, didFailWithError error: Error!) {
|
|
print("Route error: \(error.localizedDescription)")
|
|
}
|
|
}
|