// // QSLHomeController.swift // QuickSearchLocation // // Created by mac on 2024/4/10. // import UIKit import SwiftyJSON import MAMapKit import AMapFoundationKit import AMapLocationKit class QSLHomeController: QSLBaseController { lazy var viewModel: QSLHomeViewModel = { return QSLHomeViewModel() }() var personalModel: QSLUserModel = QSLUserModel() ///判断是不是第一次定位成功 var isFirstLocatScuess : Bool = true // var friendList: [QSLUserModel] = [QSLUserModel]() override func viewDidLoad() { super.viewDidLoad() viewModel.initUIData() setUpMap() setUpUI() loadData() NotificationCenter.default.addObserver(self, selector: #selector(loadData), name: QSLNotification.QSLLogin, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(loadData), name: QSLNotification.QSLLogout, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(loadData), name: QSLNotification.QSLRefreshFriend, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(loadData), name: QSLNotification.QSLRefreshRequest, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(requestVip), name: QSLNotification.QSLRefreshMember, object: nil) //处理第一次进入首页定位 firstEnterLocationHomepage() // if let currentWindow = UIApplication.keyWindow { // QSLHomeAddFriendView.alert(view: currentWindow) { // QSLJumpManager.shared.pushToAdd(type: .homealert) // } // } let vc = QSLActivityVipVC() self.rootViewController()?.pushVC(vc: vc) } //处理第一次进入首页定位 func firstEnterLocationHomepage() { guard UserDefaults.standard.string(forKey: "FIRST_ENTER_LOCATION_HOMEPAGE") != nil else { UserDefaults.standard.set("YES", forKey: "FIRST_ENTER_LOCATION_HOMEPAGE") UserDefaults.standard.synchronize() ///第一次进入埋点 gravityInstance?.track(QSLGravityConst.location_enter_first_homepage, properties: ["id": 03005]) return } } /// 高德地图 lazy var homeMapView = { let _mapView = MAMapView() _mapView.delegate = self _mapView.minZoomLevel = 7; _mapView.maxZoomLevel = 19; _mapView.zoomLevel = 17; _mapView.showsCompass = false _mapView.showsScale = false _mapView.isRotateCameraEnabled = false _mapView.showsUserLocation = true _mapView.userTrackingMode = .follow // 关闭显示精度圈 let represent = MAUserLocationRepresentation() represent.showsAccuracyRing = false _mapView.update(represent) let tap = UITapGestureRecognizer(target: self, action: #selector(mapTapAction)) _mapView.addGestureRecognizer(tap) return _mapView }() /// 高德地图定位 lazy var homeMapLocationM = { let _homeMapLocationM = AMapLocationManager() _homeMapLocationM.delegate = self _homeMapLocationM.distanceFilter = 100 _homeMapLocationM.locatingWithReGeocode = true _homeMapLocationM.desiredAccuracy = kCLLocationAccuracyHundredMeters _homeMapLocationM.locationTimeout = 2 _homeMapLocationM.reGeocodeTimeout = 2 _homeMapLocationM.pausesLocationUpdatesAutomatically = false _homeMapLocationM.allowsBackgroundLocationUpdates = true return _homeMapLocationM }() /// 头部定位权限弹窗 lazy var homeAuthHeaderView = { let _homeAuthHeaderView = QSLHomeAuthHeaderView() _homeAuthHeaderView.addRadius(radius: 6) return _homeAuthHeaderView }() lazy var homeButtonsView = { let _homeButtonsView = QSLHomeButtonView() return _homeButtonsView }() /// 定位按钮 lazy var homeLocateBtnView = { let _homeLocateBtnView = UIView() _homeLocateBtnView.addRadius(radius: 6) _homeLocateBtnView.effectViewWithAlpha(alpha: 1, size: CGSize(width: 40, height: 40), style: .light) return _homeLocateBtnView }() lazy var homeLocateBtn = { let _homeLocateBtn = UIButton(type: .custom) _homeLocateBtn.image(UIImage(named: "home_btn_locate")) return _homeLocateBtn }() /// 无好友和未登录时的状态 lazy var homeEmptyView: QSLHomeEmptyView = { let _homeEmptyView = QSLHomeEmptyView() _homeEmptyView.delegate = self _homeEmptyView.isHidden = true return _homeEmptyView }() lazy var homeFriendView: QSLHomeFriendView = { let topHeight = QSLConst.qsl_kStatusBarFrameH let view = QSLHomeFriendView(frame: CGRect(x: 0, y: QSLConst.qsl_kScreenH - viewModel.friendViewHeight - QSLConst.qsl_kTabbarFrameH, width: QSLConst.qsl_kScreenW, height: QSLConst.qsl_kScreenH - topHeight)) view.scrollTopHeight = topHeight - 40 view.delegate = self return view }() } // MARK: - 网络请求等 extension QSLHomeController { @objc func loadData() { // viewModel.refreshDataSoure(complete: { [unowned self] in // self.homeFriendView.viewModel = self.viewModel // let friViewTopY = QSLConst.qsl_kScreenH - viewModel.friendViewHeight - QSLConst.qsl_kTabbarFrameH // homeFriendView.scrollCenterHeight = friViewTopY // homeFriendView.snp.updateConstraints { make in // make.top.equalTo(friViewTopY) // } // }) self.onceLocation() if QSLBaseManager.shared.isLogin() { self.initSocket() } else { self.deposeSocket() } requestFriendList() requestVip() } // 请求好友列表 @objc func requestFriendList() { self.homeMapView.removeAnnotations(self.homeMapView.annotations) viewModel.friendList.removeAll() viewModel.friendLocationList.removeAll() viewModel.friendList.append(QSLBaseManager.shared.userModel) QSLNetwork().request(.friendList(dict: [:])) { response in let list = response.mapArray(QSLUserModel.self, modelKey: "data>list") self.viewModel.friendList.append(contentsOf: list) self.homeFriendView.viewModel = self.viewModel for model in list { let pointAnnotation = MAPointAnnotation() let loc = CLLocationCoordinate2D(latitude: model.location.lat, longitude: model.location.lng) pointAnnotation.coordinate = loc if model.remark.count > 0 { pointAnnotation.title = model.remark } else { pointAnnotation.title = model.phone } pointAnnotation.subtitle = "300" let pointModel = QSLMapPointModel() pointModel.userId = model.userId pointModel.pointAnnotation = pointAnnotation self.viewModel.friendLocationList.append(pointModel) if QSLBaseManager.shared.isVip() && !model.blockedMe { self.homeMapView.addAnnotation(pointAnnotation) } } } fail: { code, error in self.homeFriendView.viewModel = self.viewModel } } // 请求vip信息 @objc func requestVip() { QSLNetwork().request(.userMember(dict: [:])) { response in let memberModel = response.mapObject(QSLMemberModel.self, modelKey: "data") QSLBaseManager.shared.userModel.memberModel = memberModel if memberModel.expired { QSLBaseManager.shared.saveVipExpiredTime(time: 0) } else { QSLBaseManager.shared.saveVipExpiredTime(time: memberModel.endTimestamp) } QSLBaseManager.shared.saveUserId(id: memberModel.userId) // NotificationCenter.default.post(name: QSLNotification.QSLRefreshMember, object: nil) } fail: { code, error in } } } // MARK: - 设置地图相关方法 extension QSLHomeController: MAMapViewDelegate, AMapLocationManagerDelegate { // 初始化高德地图设置 func setUpMap() { AMapServices.shared().enableHTTPS = true AMapLocationManager.updatePrivacyShow(.didShow, privacyInfo: .didContain) AMapLocationManager.updatePrivacyAgree(.didAgree) homeMapLocationM.locatingWithReGeocode = true homeMapLocationM.startUpdatingLocation() } // 申请地图定位权限 func mapViewRequireLocationAuth(_ locationManager: CLLocationManager!) { locationManager.requestWhenInUseAuthorization() } // 地图触摸事件 @objc func mapTapAction() { if self.homeFriendView.scrollLocation != .ScrollBottom { self.homeFriendView.scrollToLocation(type: .ScrollBottom) } } // 获取一次性定位 func onceLocation() { // 先停止持续定位 self.homeMapLocationM.stopUpdatingLocation() self.homeMapLocationM.requestLocation(withReGeocode: true) { location, regeocode, error in if error != nil { self.personalModel = QSLBaseManager.shared.userModel // 获取电话和名字 // self.personalModel.phone = QSLBaseManager.shared.userModel.phone var name = "用户\(QSLBaseManager.shared.userModel.phone.suffix(4))" if !QSLBaseManager.shared.isLogin() { name = "自己" } name = "自己" self.personalModel.remark = name // 地址 self.personalModel.location.addr = "请先开启位置权限" QSLBaseManager.shared.updateUser(model: self.personalModel) if self.viewModel.friendList.isEmpty { self.viewModel.friendList.append(self.personalModel) } else { self.viewModel.friendList[0] = self.personalModel } self.homeFriendView.viewModel = self.viewModel self.homeMapLocationM.startUpdatingLocation() return } self.personalModel = QSLBaseManager.shared.userModel var name = "用户\(QSLBaseManager.shared.userModel.phone.suffix(4))" if !QSLBaseManager.shared.isLogin() { name = "自己" } name = "自己" self.personalModel.remark = name self.personalModel.location.addr = regeocode?.formattedAddress ?? "未知" self.personalModel.location.timestamp = Int(Date.secondStamp) ?? 0 if let location = location { // 地图居中 self.homeMapView.setCenter(location.coordinate, animated: true) self.homeFriendView.scrollLocation = .ScrollCenter self.personalModel.location.lat = location.coordinate.latitude self.personalModel.location.lng = location.coordinate.longitude // 速度 self.personalModel.location.speed = location.speed >= 0 ? location.speed : 0 // 朝向 self.personalModel.location.bearing = location.course >= 0 ? location.course : 0 } QSLBaseManager.shared.updateUser(model: self.personalModel) if QSLBaseManager.shared.isLogin() { let locationData = self.personalModel.location let addr = locationData.addr if addr != "请先开启位置权限" { self.sendSocket() } } if self.viewModel.friendList.isEmpty { self.viewModel.friendList.append(self.personalModel) } else { self.viewModel.friendList[0] = self.personalModel } print("location: \(String(describing: location))") if let regeocode = regeocode { print("reGeocode: \(regeocode)") } self.homeFriendView.viewModel = self.viewModel self.homeMapLocationM.startUpdatingLocation() } } // 持续定位 func amapLocationManager(_ manager: AMapLocationManager!, didUpdate location: CLLocation!, reGeocode: AMapLocationReGeocode!) { if self.isFirstLocatScuess { self.isFirstLocatScuess = false //始终允许定位成功 gravityInstance?.track(QSLGravityConst.location_success_always, properties: ["id": 03003]) } // 获取电话和名字 self.personalModel = QSLBaseManager.shared.userModel var name = "用户\(QSLBaseManager.shared.userModel.phone.suffix(4))" if !QSLBaseManager.shared.isLogin() { name = "自己" } name = "自己" self.personalModel.remark = name // 时间戳 self.personalModel.location.timestamp = Int(Date.secondStamp) ?? 0 // 经纬度 self.personalModel.location.lat = location.coordinate.latitude self.personalModel.location.lng = location.coordinate.longitude // 速度 if location.speed >= 0 { self.personalModel.location.speed = location.speed } else { self.personalModel.location.speed = 0 } // 朝向 if location.course >= 0 { self.personalModel.location.bearing = location.course } else { self.personalModel.location.bearing = 0 } // 打印位置信息 print("location: {lat: \(location.coordinate.latitude); lon: \(location.coordinate.longitude); accuracy: \(location.horizontalAccuracy)}") // 逆地理编码处理 if let reGeocode = reGeocode { print("reGeocode: \(reGeocode)") self.personalModel.location.addr = reGeocode.formattedAddress } else { self.personalModel.location.addr = "请先开启位置权限" } // 保存到模块 QSLBaseManager.shared.updateUser(model: self.personalModel) // 更新好友列表 if self.viewModel.friendList.isEmpty { self.viewModel.friendList.append(self.personalModel) } else { self.viewModel.friendList[0] = self.personalModel } self.homeFriendView.viewModel = self.viewModel // 更新UI // self.updateHomeUI() // 如果已登录,发送定位信息 if QSLBaseManager.shared.isLogin() { let locationData = self.personalModel.location let addr = locationData.addr if addr != "请先开启位置权限" { self.sendSocket() } } } func mapView(_ mapView: MAMapView!, viewFor annotation: (any MAAnnotation)!) -> MAAnnotationView! { // 处理用户位置的标注 if annotation is MAUserLocation { let reuseIdentifier = "UserAnotation" var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) as? QSLHomeAnnotatinView if annotationView == nil { annotationView = QSLHomeAnnotatinView(annotation: annotation, reuseIdentifier: reuseIdentifier) } let name = "我" annotationView?.title = name annotationView?.isEnabled = false annotationView?.image = UIImage(named: "home_mine_anno") annotationView?.canShowCallout = false annotationView?.centerOffset = CGPoint(x: 0, y: -18) annotationView?.calloutOffset = CGPoint(x: 0, y: 0) annotationView?.zIndex = 360 annotationView?.isSelected = true return annotationView } // 处理普通点标注 if annotation is MAPointAnnotation { let reuseIdentifier = "OtherAnnotation" var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) as? QSLHomeAnnotatinView if annotationView == nil { annotationView = QSLHomeAnnotatinView(annotation: annotation, reuseIdentifier: reuseIdentifier) annotationView?.title = annotation.title } if let subtitle = annotation.subtitle as? Int { annotationView?.zIndex = subtitle } annotationView?.isEnabled = false annotationView?.image = UIImage(named: "home_mine_anno") annotationView?.canShowCallout = false annotationView?.centerOffset = CGPoint(x: 0, y: -18) annotationView?.calloutOffset = CGPoint(x: 0, y: 0) annotationView?.isSelected = true return annotationView } return nil } //@brief 定位权限状态改变时回调函数。注意:iOS13及之前版本回调 // 高德地图权限变化回调 func amapLocationManager(_ manager: AMapLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { //print("高德地图定位权限变化: \(status.rawValue)") switch status { case .authorizedAlways, .authorizedWhenInUse: //print("定位权限已授权") //支付成功埋点 gravityInstance?.track(QSLGravityConst.location_allow_permission, properties: ["id": 03001]) case .denied: //print("定位权限被拒绝") //拒绝定位权限 gravityInstance?.track(QSLGravityConst.location_deny_permission, properties: ["id": 03002]) default: break } } // 系统定位权限变化回调 func amapLocationManager(_ manager: AMapLocationManager, locationManagerDidChangeAuthorization locationManager: CLLocationManager) { if #available(iOS 14.0, *) { let status = locationManager.authorizationStatus // 统一处理权限变化 amapLocationManager(manager, didChangeAuthorization: status) //print("系统定位权限变化: \(status.rawValue)") } else { // Fallback on earlier versions } } // 定位结果回调 func amapLocationManager(_ manager: AMapLocationManager, didFailWithError error: Error) { print("定位失败: \(error.localizedDescription)") gravityInstance?.track(QSLGravityConst.location_fail_always, properties: ["id": 03004]) } } // MARK: - 设置WebSocket extension QSLHomeController: QSLSocketManagerDelegate { // 初始化 func initSocket() { QSLSocketManager.shared.urlString = "\(QSLApi.prodWSUrl)/websocket/\(QSLBaseManager.shared.userModel.authToken)" QSLSocketManager.shared.connect() QSLSocketManager.shared.delegate = self } // 关闭socket func deposeSocket() { QSLSocketManager.shared.urlString = "" QSLSocketManager.shared.close() QSLSocketManager.shared.delegate = nil } // 发送socket信息 func sendSocket() { var message = QSLMapMessageModel() message.cmd = "u.location" var location = QSLMapTrackModel() location.lat = self.personalModel.location.lat location.lng = self.personalModel.location.lng location.addr = self.personalModel.location.addr location.speed = self.personalModel.location.speed location.bearing = self.personalModel.location.bearing location.timestamp = Int(Date.milliStamp) ?? 0 message.body = location.toJSONString() QSLSocketManager.shared.sendMessage(message.toJSONString()) } // 接收到消息 func socketDidReceiveMessage(with string: String) { let locationModel = QSLMapMessageModel.mapModel(from: string) if let data = locationModel.body.data(using: .utf8), let list = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [QSLMapTrackModel] { if locationModel.cmd == "d.location.batch" { for model in list { var isBlockedMe = false for var user in viewModel.friendList { if model.userId == user.friendId { user.location = model isBlockedMe = user.blockedMe } } if !isBlockedMe && QSLBaseManager.shared.isVip() { for location in viewModel.friendLocationList { if model.userId == location.userId { let annotation = MAPointAnnotation() let cll2d = CLLocationCoordinate2D(latitude: model.lat, longitude: model.lng) annotation.coordinate = cll2d location.pointAnnotation = annotation self.homeMapView.addAnnotation(location.pointAnnotation) } } } } self.homeFriendView.viewModel = self.viewModel } } } } // MARK: - 设置界面代理方法 extension QSLHomeController: QSLHomeEmptyViewDelegate, QSLHomeFriendViewDelegate { func viewDidScroll() { switch self.homeFriendView.scrollLocation { case .ScrollTop: self.homeFriendView.snp.updateConstraints { make in make.top.equalTo(QSLConst.qsl_kStatusBarFrameH - 40.rpx) } break case .ScrollCenter: self.homeFriendView.snp.updateConstraints { make in make.top.equalTo(QSLConst.qsl_kScreenH - viewModel.friendViewHeight - QSLConst.qsl_kStatusBarFrameH) } break case .ScrollBottom: self.homeFriendView.snp.updateConstraints { make in make.top.equalTo(QSLConst.qsl_kScreenH - QSLConst.qsl_kTabbarFrameH - 56.rpx - 40.rpx) } break default: break } } func addFriendAction(isSmall: Bool) { if isSmall { QSLJumpManager.shared.pushToAdd(type: .homesmall) } else { QSLJumpManager.shared.pushToAdd(type: .homebig) } } func refreshBtnAction() { gravityInstance?.track(QSLGravityConst.home_locate) self.loadData() } // 轨迹 func routeBtnAction(model: QSLUserModel) { if !QSLBaseManager.shared.isLogin() { if let view = self.tabBarController?.view { QSLAlertView.alert(view: view, title: "温馨提示", content: "登录即可体验查看轨迹记录", secondBtnClosure: { QSLJumpManager.shared.pushToLogin(type: .road) }) } return } if !QSLBaseManager.shared.isVip() { QSLJumpManager.shared.pushToVip(type: .homeRoad) return } QSLJumpManager.shared.pushToRoad(type: .home, model: model) } // 点击列表的定位按钮 func locateBtnAction(model: QSLUserModel) { if !QSLBaseManager.shared.isVip() { self.view.toast(text: "请先开通会员") return } if model.blockedMe { return } let location = CLLocationCoordinate2D(latitude: model.location.lat, longitude: model.location.lng) self.homeMapView.setCenter(location, animated: true) for point in self.viewModel.friendLocationList { if point.userId == model.friendId { self.homeMapView.removeAnnotation(point.pointAnnotation) point.pointAnnotation?.subtitle = "350" self.homeMapView.addAnnotation(point.pointAnnotation) } } } func emptyFriPhoneViewClick() { if let vc = self.tabBarController { QSLFriendAddAlertView.show(vc: vc) { } } } func homeFriPhoneViewClick() { if let vc = self.tabBarController { QSLFriendAddAlertView.show(vc: vc) { } } } } // MARK: - 设置UI相关方法 extension QSLHomeController { // 设置基础UI func setUpUI() { view.addSubview(homeMapView) homeMapView.snp.makeConstraints { make in make.edges.equalTo(0) } // view.addSubview(homeButtonsView) // homeButtonsView.snp.makeConstraints { make in // make.size.equalTo(CGSize(width: 40, height: 104)) // make.top.equalTo(QSLConst.qsl_kStatusBarFrameH + 164) // make.right.equalTo(QSLHomeViewModel.UX.viewRight) // } // // view.addSubview(homeLocateBtnView) // homeLocateBtnView.snp.makeConstraints { make in // make.size.equalTo(CGSize(width: 40, height: 40)) // make.top.equalTo(self.homeButtonsView.snp.bottom).offset(16) // make.right.equalTo(QSLHomeViewModel.UX.viewRight) // } view.addSubview(homeFriendView) homeFriendView.snp.makeConstraints { make in make.left.right.equalTo(0) make.top.equalTo(QSLConst.qsl_kScreenH - viewModel.friendViewHeight - QSLConst.qsl_kTabbarFrameH) make.height.equalTo(QSLConst.qsl_kScreenH - QSLConst.qsl_kTabbarFrameH) } let friViewTopY = QSLConst.qsl_kScreenH - viewModel.friendViewHeight - QSLConst.qsl_kTabbarFrameH homeFriendView.scrollCenterHeight = friViewTopY homeFriendView.snp.updateConstraints { make in make.top.equalTo(friViewTopY) } } }