mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Display user heading in location picker and location view
This commit is contained in:
@@ -71,6 +71,8 @@
|
|||||||
bool _throttle;
|
bool _throttle;
|
||||||
TGLocationPinAnnotationView *_ownLiveLocationView;
|
TGLocationPinAnnotationView *_ownLiveLocationView;
|
||||||
__weak MKAnnotationView *_userLocationView;
|
__weak MKAnnotationView *_userLocationView;
|
||||||
|
|
||||||
|
UIImageView *_headingArrowView;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -162,6 +164,8 @@
|
|||||||
[_liveLocationsDisposable dispose];
|
[_liveLocationsDisposable dispose];
|
||||||
[_reloadDisposable dispose];
|
[_reloadDisposable dispose];
|
||||||
[_frequentUpdatesDisposable dispose];
|
[_frequentUpdatesDisposable dispose];
|
||||||
|
|
||||||
|
[_locationManager stopUpdatingHeading];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)tg_setRightBarButtonItem:(UIBarButtonItem *)barButtonItem action:(bool)action animated:(bool)animated {
|
- (void)tg_setRightBarButtonItem:(UIBarButtonItem *)barButtonItem action:(bool)action animated:(bool)animated {
|
||||||
@@ -438,6 +442,36 @@
|
|||||||
{
|
{
|
||||||
[super loadView];
|
[super loadView];
|
||||||
|
|
||||||
|
static UIImage *headingArrowImage = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
|
||||||
|
dispatch_once(&onceToken, ^
|
||||||
|
{
|
||||||
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(28.0f, 28.0f), false, 0.0f);
|
||||||
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
|
||||||
|
CGContextClearRect(context, CGRectMake(0, 0, 28, 28));
|
||||||
|
|
||||||
|
CGContextSetFillColorWithColor(context, UIColorRGB(0x3393fe).CGColor);
|
||||||
|
|
||||||
|
CGContextMoveToPoint(context, 14, 0);
|
||||||
|
CGContextAddLineToPoint(context, 19, 7);
|
||||||
|
CGContextAddLineToPoint(context, 9, 7);
|
||||||
|
CGContextClosePath(context);
|
||||||
|
CGContextFillPath(context);
|
||||||
|
|
||||||
|
CGContextSetBlendMode(context, kCGBlendModeClear);
|
||||||
|
CGContextFillEllipseInRect(context, CGRectMake(5.0, 5.0, 18.0, 18.0));
|
||||||
|
|
||||||
|
headingArrowImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
});
|
||||||
|
|
||||||
|
_headingArrowView = [[UIImageView alloc] init];
|
||||||
|
_headingArrowView.hidden = true;
|
||||||
|
_headingArrowView.frame = CGRectMake(0.0, 0.0, 28.0, 28.0);
|
||||||
|
_headingArrowView.image = headingArrowImage;
|
||||||
|
|
||||||
_tableView.scrollsToTop = false;
|
_tableView.scrollsToTop = false;
|
||||||
_mapView.tapEnabled = false;
|
_mapView.tapEnabled = false;
|
||||||
|
|
||||||
@@ -495,6 +529,8 @@
|
|||||||
{
|
{
|
||||||
[super viewWillAppear:animated];
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
|
[_locationManager startUpdatingHeading];
|
||||||
|
|
||||||
if (self.previewMode && !animated)
|
if (self.previewMode && !animated)
|
||||||
{
|
{
|
||||||
UIView *contentView = [[_mapView subviews] firstObject];
|
UIView *contentView = [[_mapView subviews] firstObject];
|
||||||
@@ -950,6 +986,9 @@
|
|||||||
{
|
{
|
||||||
_userLocationView = view;
|
_userLocationView = view;
|
||||||
|
|
||||||
|
[_userLocationView addSubview:_headingArrowView];
|
||||||
|
_headingArrowView.center = CGPointMake(view.frame.size.width / 2.0, view.frame.size.height / 2.0);
|
||||||
|
|
||||||
if (_ownLiveLocationView != nil)
|
if (_ownLiveLocationView != nil)
|
||||||
{
|
{
|
||||||
[_userLocationView addSubview:_ownLiveLocationView];
|
[_userLocationView addSubview:_ownLiveLocationView];
|
||||||
@@ -982,6 +1021,14 @@
|
|||||||
return CLLocationCoordinate2DMake(_locationAttachment.latitude, _locationAttachment.longitude);
|
return CLLocationCoordinate2DMake(_locationAttachment.latitude, _locationAttachment.longitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
|
||||||
|
{
|
||||||
|
if (newHeading != nil) {
|
||||||
|
_headingArrowView.hidden = false;
|
||||||
|
_headingArrowView.transform = CGAffineTransformMakeRotation(newHeading.magneticHeading / 180.0 * M_PI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||||
|
|||||||
@@ -68,11 +68,30 @@ private class LocationMapView: MKMapView, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func generateHeadingArrowImage() -> UIImage? {
|
||||||
|
return generateImage(CGSize(width: 28.0, height: 28.0)) { size, context in
|
||||||
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||||
|
context.clear(bounds)
|
||||||
|
|
||||||
|
context.setFillColor(UIColor(rgb: 0x3393fe).cgColor)
|
||||||
|
|
||||||
|
context.move(to: CGPoint(x: 14.0, y: 0.0))
|
||||||
|
context.addLine(to: CGPoint(x: 19.0, y: 7.0))
|
||||||
|
context.addLine(to: CGPoint(x: 9.0, y: 7.0))
|
||||||
|
context.closePath()
|
||||||
|
context.fillPath()
|
||||||
|
|
||||||
|
context.setBlendMode(.clear)
|
||||||
|
context.fillEllipse(in: bounds.insetBy(dx: 5.0, dy: 5.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
||||||
private let locationPromise = Promise<CLLocation?>(nil)
|
private let locationPromise = Promise<CLLocation?>(nil)
|
||||||
|
|
||||||
private let pickerAnnotationContainerView: PickerAnnotationContainerView
|
private let pickerAnnotationContainerView: PickerAnnotationContainerView
|
||||||
private weak var userLocationAnnotationView: MKAnnotationView?
|
private weak var userLocationAnnotationView: MKAnnotationView?
|
||||||
|
private var headingArrowView: UIImageView?
|
||||||
|
|
||||||
private let pinDisposable = MetaDisposable()
|
private let pinDisposable = MetaDisposable()
|
||||||
|
|
||||||
@@ -103,6 +122,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
override func didLoad() {
|
override func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
|
self.headingArrowView = UIImageView()
|
||||||
|
self.headingArrowView?.frame = CGRect(origin: CGPoint(), size: CGSize(width: 28.0, height: 28.0))
|
||||||
|
self.headingArrowView?.image = generateHeadingArrowImage()
|
||||||
|
|
||||||
self.mapView?.interactiveTransitionGestureRecognizerTest = { p in
|
self.mapView?.interactiveTransitionGestureRecognizerTest = { p in
|
||||||
if p.x > 44.0 {
|
if p.x > 44.0 {
|
||||||
return true
|
return true
|
||||||
@@ -232,6 +255,10 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
for view in views {
|
for view in views {
|
||||||
if view.annotation is MKUserLocation {
|
if view.annotation is MKUserLocation {
|
||||||
self.userLocationAnnotationView = view
|
self.userLocationAnnotationView = view
|
||||||
|
if let headingArrowView = self.headingArrowView {
|
||||||
|
view.addSubview(headingArrowView)
|
||||||
|
headingArrowView.center = CGPoint(x: view.frame.width / 2.0, y: view.frame.height / 2.0)
|
||||||
|
}
|
||||||
if let annotationView = self.customUserLocationAnnotationView {
|
if let annotationView = self.customUserLocationAnnotationView {
|
||||||
view.addSubview(annotationView)
|
view.addSubview(annotationView)
|
||||||
}
|
}
|
||||||
@@ -347,6 +374,18 @@ final class LocationMapNode: ASDisplayNode, MKMapViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var userHeading: CGFloat? = nil {
|
||||||
|
didSet {
|
||||||
|
if let heading = self.userHeading {
|
||||||
|
self.headingArrowView?.isHidden = false
|
||||||
|
self.headingArrowView?.transform = CGAffineTransform(rotationAngle: CGFloat(heading / 180.0 * CGFloat.pi))
|
||||||
|
} else {
|
||||||
|
self.headingArrowView?.isHidden = true
|
||||||
|
self.headingArrowView?.transform = CGAffineTransform.identity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var annotations: [LocationPinAnnotation] = [] {
|
var annotations: [LocationPinAnnotation] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
guard let mapView = self.mapView else {
|
guard let mapView = self.mapView else {
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ public final class LocationPickerController: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction)
|
self.displayNode = LocationPickerControllerNode(context: self.context, presentationData: self.presentationData, mode: self.mode, interaction: interaction, locationManager: self.locationManager)
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
self.permissionDisposable = (DeviceAccess.authorizationStatus(subject: .location(.send))
|
self.permissionDisposable = (DeviceAccess.authorizationStatus(subject: .location(.send))
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import AppBundle
|
|||||||
import CoreLocation
|
import CoreLocation
|
||||||
import Geocoding
|
import Geocoding
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
|
import DeviceAccess
|
||||||
|
|
||||||
private struct LocationPickerTransaction {
|
private struct LocationPickerTransaction {
|
||||||
let deletions: [ListViewDeleteItem]
|
let deletions: [ListViewDeleteItem]
|
||||||
@@ -240,12 +241,13 @@ struct LocationPickerState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class LocationPickerControllerNode: ViewControllerTracingNode {
|
final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationManagerDelegate {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private let presentationDataPromise: Promise<PresentationData>
|
private let presentationDataPromise: Promise<PresentationData>
|
||||||
private let mode: LocationPickerMode
|
private let mode: LocationPickerMode
|
||||||
private let interaction: LocationPickerInteraction
|
private let interaction: LocationPickerInteraction
|
||||||
|
private let locationManager: LocationManager
|
||||||
|
|
||||||
private let listNode: ListView
|
private let listNode: ListView
|
||||||
private let emptyResultsTextNode: ImmediateTextNode
|
private let emptyResultsTextNode: ImmediateTextNode
|
||||||
@@ -269,12 +271,13 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
|
|||||||
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)?
|
||||||
private var listOffset: CGFloat?
|
private var listOffset: CGFloat?
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction) {
|
init(context: AccountContext, presentationData: PresentationData, mode: LocationPickerMode, interaction: LocationPickerInteraction, locationManager: LocationManager) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.presentationDataPromise = Promise(presentationData)
|
self.presentationDataPromise = Promise(presentationData)
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.interaction = interaction
|
self.interaction = interaction
|
||||||
|
self.locationManager = locationManager
|
||||||
|
|
||||||
self.state = LocationPickerState()
|
self.state = LocationPickerState()
|
||||||
self.statePromise = Promise(self.state)
|
self.statePromise = Promise(self.state)
|
||||||
@@ -539,7 +542,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
|
|||||||
switch previousState.selectedLocation {
|
switch previousState.selectedLocation {
|
||||||
case .none, .venue:
|
case .none, .venue:
|
||||||
updateMap = true
|
updateMap = true
|
||||||
case let .location(previousCoordinate, address):
|
case let .location(previousCoordinate, _):
|
||||||
if previousCoordinate != coordinate {
|
if previousCoordinate != coordinate {
|
||||||
updateMap = true
|
updateMap = true
|
||||||
}
|
}
|
||||||
@@ -691,11 +694,20 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
|
|||||||
strongSelf.goToUserLocation()
|
strongSelf.goToUserLocation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.locationManager.manager.startUpdatingHeading()
|
||||||
|
self.locationManager.manager.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.disposable?.dispose()
|
self.disposable?.dispose()
|
||||||
self.geocodingDisposable.dispose()
|
self.geocodingDisposable.dispose()
|
||||||
|
|
||||||
|
self.locationManager.manager.stopUpdatingHeading()
|
||||||
|
}
|
||||||
|
|
||||||
|
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
|
||||||
|
self.headerNode.mapNode.userHeading = CGFloat(newHeading.magneticHeading)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePresentationData(_ presentationData: PresentationData) {
|
func updatePresentationData(_ presentationData: PresentationData) {
|
||||||
@@ -727,7 +739,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func dequeueTransition() {
|
private func dequeueTransition() {
|
||||||
guard let layout = self.validLayout, let transition = self.enqueuedTransitions.first else {
|
guard let _ = self.validLayout, let transition = self.enqueuedTransitions.first else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.enqueuedTransitions.remove(at: 0)
|
self.enqueuedTransitions.remove(at: 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user