From 24e18d6f7a95066959522cb21a5dfe41662b8813 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 27 Oct 2020 20:16:42 +0400 Subject: [PATCH] Location view fixes --- .../Sources/DeviceLocationManager.swift | 1 + .../ChatMessageLiveLocationPositionNode.swift | 13 +++-- .../Sources/LocationAnnotation.swift | 57 +++++++++++++++++-- .../LocationUI/Sources/LocationMapNode.swift | 41 +++++++++++++ 4 files changed, 100 insertions(+), 12 deletions(-) diff --git a/submodules/DeviceLocationManager/Sources/DeviceLocationManager.swift b/submodules/DeviceLocationManager/Sources/DeviceLocationManager.swift index 1a20f4e2c1..839f28e184 100644 --- a/submodules/DeviceLocationManager/Sources/DeviceLocationManager.swift +++ b/submodules/DeviceLocationManager/Sources/DeviceLocationManager.swift @@ -59,6 +59,7 @@ public final class DeviceLocationManager: NSObject { self.manager.distanceFilter = 5.0 self.manager.activityType = .other self.manager.pausesLocationUpdatesAutomatically = false + self.manager.headingFilter = 2.0 } public func push(mode: DeviceLocationMode, updated: @escaping (CLLocation, Double?) -> Void) -> Disposable { diff --git a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift index 2d6429d89e..4c671c379f 100644 --- a/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift +++ b/submodules/LiveLocationPositionNode/Sources/ChatMessageLiveLocationPositionNode.swift @@ -147,12 +147,17 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode { var hasPulse = false var heading: Double? var coordinate: (Double, Double)? + + func degToRad(_ degrees: Double) -> Double { + return degrees * Double.pi / 180.0 + } + switch mode { case let .liveLocation(_, active, latitude, longitude, headingValue): backgroundImage = avatarBackgroundImage hasPulse = active coordinate = (latitude, longitude) - heading = headingValue.flatMap { Double($0) } + heading = headingValue.flatMap { degToRad(Double($0)) } case let .location(location): let venueType = location?.venue?.type ?? "" let color = venueType.isEmpty ? theme.list.itemAccentColor : venueIconColor(type: venueType) @@ -162,10 +167,6 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode { } } - func degToRad(_ degrees: Double) -> Double { - return degrees * Double.pi / 180.0 - } - if heading == nil, let currentCoordinate = currentCoordinate, let coordinate = coordinate { let lat1 = degToRad(currentCoordinate.0) let lon1 = degToRad(currentCoordinate.1) @@ -248,7 +249,7 @@ public final class ChatMessageLiveLocationPositionNode: ASDisplayNode { strongSelf.arrowNode.isHidden = heading == nil || !hasPulse strongSelf.arrowNode.position = CGPoint(x: 31.0, y: 64.0) - strongSelf.arrowNode.transform = CATransform3DMakeRotation(CGFloat(heading ?? 0.0 / 180.0 * Double.pi), 0.0, 0.0, 1.0) + strongSelf.arrowNode.transform = CATransform3DMakeRotation(CGFloat(heading ?? 0), 0.0, 0.0, 1.0) } }) } diff --git a/submodules/LocationUI/Sources/LocationAnnotation.swift b/submodules/LocationUI/Sources/LocationAnnotation.swift index 0a66824904..5a7cffa715 100644 --- a/submodules/LocationUI/Sources/LocationAnnotation.swift +++ b/submodules/LocationUI/Sources/LocationAnnotation.swift @@ -43,7 +43,7 @@ class LocationPinAnnotation: NSObject, MKAnnotation { let peer: Peer? let message: Message? let forcedSelection: Bool - var heading: Int32? { + @objc dynamic var heading: NSNumber? { willSet { self.willChangeValue(forKey: "heading") } @@ -91,7 +91,7 @@ class LocationPinAnnotation: NSObject, MKAnnotation { } self.selfPeer = selfPeer self.forcedSelection = false - self.heading = heading + self.heading = heading.flatMap { NSNumber(value: $0) } super.init() } @@ -167,6 +167,8 @@ class LocationPinAnnotationView: MKAnnotationView { var hasPulse = false + var headingKvoToken: NSKeyValueObservation? + override class var layerClass: AnyClass { return LocationPinAnnotationLayer.self } @@ -233,6 +235,14 @@ class LocationPinAnnotationView: MKAnnotationView { self.annotation = annotation } + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.headingKvoToken?.invalidate() + } + var defaultZPosition: CGFloat { if let annotation = self.annotation as? LocationPinAnnotation { if annotation.forcedSelection { @@ -247,10 +257,6 @@ class LocationPinAnnotationView: MKAnnotationView { } } - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - override var annotation: MKAnnotation? { didSet { if let annotation = self.annotation as? LocationPinAnnotation { @@ -270,6 +276,18 @@ class LocationPinAnnotationView: MKAnnotationView { self.shadowNode.isHidden = true self.smallNode.isHidden = false } + + if let headingKvoToken = self.headingKvoToken { + self.headingKvoToken = nil + headingKvoToken.invalidate() + } + + self.headingKvoToken = annotation.observe(\.heading, options: .new) { [weak self] (_, change) in + guard let heading = change.newValue else { + return + } + self?.updateHeading(heading) + } } else if let peer = annotation.peer { self.iconNode.isHidden = true @@ -278,6 +296,12 @@ class LocationPinAnnotationView: MKAnnotationView { self.setPeer(context: annotation.context, theme: annotation.theme, peer: peer) self.setSelected(true, animated: false) + + if let headingKvoToken = self.headingKvoToken { + self.headingKvoToken = nil + headingKvoToken.invalidate() + } + self.updateHeading(nil) } else if let location = annotation.location { let venueType = location.venue?.type ?? "" let color = venueType.isEmpty ? annotation.theme.list.itemAccentColor : venueIconColor(type: venueType) @@ -299,15 +323,36 @@ class LocationPinAnnotationView: MKAnnotationView { self.setSelected(true, animated: false) } + if let avatarNode = self.avatarNode { + self.avatarNode = nil + avatarNode.removeFromSupernode() + } + if self.initialized && !self.appeared { self.appeared = true self.animateAppearance() } + + if let headingKvoToken = self.headingKvoToken { + self.headingKvoToken = nil + headingKvoToken.invalidate() + } + self.updateHeading(nil) } } } } + private func updateHeading(_ heading: NSNumber?) { + if let heading = heading?.int32Value { + self.arrowNode.isHidden = false + self.arrowNode.transform = CATransform3DMakeRotation(CGFloat(heading) / 180.0 * CGFloat.pi, 0.0, 0.0, 1.0) + } else { + self.arrowNode.isHidden = true + self.arrowNode.transform = CATransform3DIdentity + } + } + override func prepareForReuse() { self.smallNode.isHidden = true self.backgroundNode.isHidden = false diff --git a/submodules/LocationUI/Sources/LocationMapNode.swift b/submodules/LocationUI/Sources/LocationMapNode.swift index 7ec44d33e8..114df8d0b7 100644 --- a/submodules/LocationUI/Sources/LocationMapNode.swift +++ b/submodules/LocationUI/Sources/LocationMapNode.swift @@ -372,6 +372,11 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { var trackingMode: LocationTrackingMode = .none { didSet { self.mapView?.userTrackingMode = self.trackingMode.userTrackingMode + if self.trackingMode == .followWithHeading && self.headingArrowView?.image != nil { + self.headingArrowView?.image = nil + } else if self.trackingMode != .followWithHeading && self.headingArrowView?.image == nil { + self.headingArrowView?.image = generateHeadingArrowImage() + } } } @@ -676,9 +681,45 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate { } if let updatedAnnotation = dict[annotation.id] { + func degToRad(_ degrees: Double) -> Double { + return degrees * Double.pi / 180.0 + } + + func radToDeg(_ radians: Double) -> Double { + return radians / Double.pi * 180.0 + } + + let currentCoordinate = annotation.coordinate + let coordinate = updatedAnnotation.coordinate + var heading = updatedAnnotation.heading + if heading == nil { + let previous = CLLocation(latitude: currentCoordinate.latitude, longitude: currentCoordinate.longitude) + let new = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) + + if new.distance(from: previous) > 10 { + let lat1 = degToRad(currentCoordinate.latitude) + let lon1 = degToRad(currentCoordinate.longitude) + let lat2 = degToRad(coordinate.latitude) + let lon2 = degToRad(coordinate.longitude) + + let dLat = lat2 - lat1 + let dLon = lon2 - lon1 + + if dLat != 0 && dLon != 0 { + let y = sin(dLon) * cos(lat2) + let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon) + heading = NSNumber(value: radToDeg(atan2(y, x))) + } + } else { + heading = annotation.heading + } + } + UIView.animate(withDuration: 0.2) { annotation.coordinate = updatedAnnotation.coordinate } + + annotation.heading = heading dict[annotation.id] = nil } else { annotationsToRemove.insert(annotation)