jsdw_ios/QuickLocation/Section/Home/GroupSchedule/GroupScheduleVC.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)")
}
}