mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-19 04:00:54 +00:00
1501 lines
55 KiB
Objective-C
1501 lines
55 KiB
Objective-C
#import "TGLocationPickerController.h"
|
|
|
|
#import "LegacyComponentsInternal.h"
|
|
#import "TGColor.h"
|
|
|
|
#import <MapKit/MapKit.h>
|
|
|
|
#import "TGLocationUtils.h"
|
|
|
|
#import "TGLocationSignals.h"
|
|
|
|
#import "TGListsTableView.h"
|
|
#import "TGSearchBar.h"
|
|
#import "TGSearchDisplayMixin.h"
|
|
#import <LegacyComponents/TGModernButton.h>
|
|
#import <LegacyComponents/TGModernBarButton.h>
|
|
#import <LegacyComponents/UIControl+HitTestEdgeInsets.h>
|
|
|
|
#import "TGLocationAnnotation.h"
|
|
#import "TGLocationReverseGeocodeResult.h"
|
|
|
|
#import "TGLocationMapView.h"
|
|
#import "TGLocationPinView.h"
|
|
#import "TGPickPinAnnotationView.h"
|
|
|
|
#import "TGLocationVenue.h"
|
|
|
|
#import "TGLocationVenueCell.h"
|
|
#import "TGLocationCurrentLocationCell.h"
|
|
#import "TGLocationSectionHeaderCell.h"
|
|
#import "TGLocationTrackingButton.h"
|
|
#import "TGLocationMapModeControl.h"
|
|
|
|
const CGFloat TGLocationPickerMapClipHeight = 1600.0f;
|
|
const CGFloat TGLocationPickerMapWidescreenHeight = 342.0f;
|
|
const CGFloat TGLocationPickerMapHeight = 265.0f;
|
|
const CGFloat TGLocationPickerMapInset = 280.0f;
|
|
const MKCoordinateSpan TGLocationDefaultSpan = { 0.008, 0.008 };
|
|
const CGPoint TGLocationPickerPinOffset = { 0.0f, 33.0f };
|
|
|
|
const TGLocationPlacesService TGLocationPickerPlacesProvider = TGLocationPlacesServiceFoursquare;
|
|
|
|
@interface TGLocationPair : NSObject
|
|
|
|
@property (nonatomic, readonly) CLLocation *location;
|
|
@property (nonatomic, readonly, getter=isCurrent) bool current;
|
|
@property (nonatomic, assign) bool onlyLocationUpdate;
|
|
|
|
@end
|
|
|
|
@implementation TGLocationPair
|
|
|
|
+ (TGLocationPair *)pairWithLocation:(CLLocation *)location isCurrent:(bool)isCurrent onlyLocationUpdate:(bool)onlyLocationUpdate
|
|
{
|
|
TGLocationPair *pair = [[TGLocationPair alloc] init];
|
|
pair->_location = location;
|
|
pair->_current = isCurrent;
|
|
pair->_onlyLocationUpdate = onlyLocationUpdate;
|
|
return pair;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface TGLocationPickerController () <UITableViewDataSource, UITableViewDelegate, MKMapViewDelegate, TGSearchBarDelegate, TGSearchDisplayMixinDelegate>
|
|
{
|
|
TGLocationPickerControllerIntent _intent;
|
|
|
|
CLLocationManager *_locationManager;
|
|
bool _locationServicesDisabled;
|
|
|
|
bool _nearbyVenuesLoadFailed;
|
|
NSArray *_nearbyVenues;
|
|
NSArray *_searchResults;
|
|
NSString *_searchResultsQuery;
|
|
|
|
TGLocationAnnotation *_annotation;
|
|
TGLocationAnnotation *_customAnnotation;
|
|
|
|
CLLocation *_currentUserLocation;
|
|
CLLocation *_startLocation;
|
|
CLLocation *_venuesFetchLocation;
|
|
SMetaDisposable *_locationUpdateDisposable;
|
|
void (^_userLocationObserver)(CLLocation *location);
|
|
|
|
SMetaDisposable *_nearbyVenuesDisposable;
|
|
SMetaDisposable *_searchDisposable;
|
|
SMetaDisposable *_reverseGeocodeDisposable;
|
|
|
|
UIView *_pickerPinWrapper;
|
|
TGLocationPinView *_pickerPinView;
|
|
TGPickPinAnnotationView *_pickerAnnotationView;
|
|
|
|
NSValue *_fullScreenMapSpan;
|
|
|
|
UIView *_mapClipView;
|
|
UIView *_mapViewWrapper;
|
|
TGLocationMapView *_mapView;
|
|
bool _mapInFullScreenMode;
|
|
bool _pinMovedFromUserLocation;
|
|
bool _updatePinAnnotation;
|
|
|
|
CGFloat _tableViewTopInset;
|
|
UITableView *_nearbyVenuesTableView;
|
|
|
|
UIView *_searchBarOverlay;
|
|
UIBarButtonItem *_searchButtonItem;
|
|
UIView *_searchReferenceView;
|
|
UIView *_searchBarWrapper;
|
|
TGSearchBar *_searchBar;
|
|
TGSearchDisplayMixin *_searchMixin;
|
|
|
|
UIActivityIndicatorView *_activityIndicator;
|
|
UILabel *_messageLabel;
|
|
|
|
UIView *_toolbarWrapperView;
|
|
UIView *_toolbarView;
|
|
TGModernButton *_userLocationButton;
|
|
TGModernButton *_showPlacesButton;
|
|
UIImageView *_mapModeControlMask;
|
|
TGLocationMapModeControl *_mapModeControl;
|
|
TGModernButton *_mapModeButton;
|
|
|
|
UIImageView *_attributionView;
|
|
}
|
|
@end
|
|
|
|
@implementation TGLocationPickerController
|
|
|
|
- (instancetype)init
|
|
{
|
|
return [self initWithIntent:TGLocationPickerControllerDefaultIntent];
|
|
}
|
|
|
|
- (instancetype)initWithIntent:(TGLocationPickerControllerIntent)intent
|
|
{
|
|
self = [super init];
|
|
if (self != nil)
|
|
{
|
|
_intent = intent;
|
|
_locationManager = [[CLLocationManager alloc] init];
|
|
|
|
_locationUpdateDisposable = [[SMetaDisposable alloc] init];
|
|
_nearbyVenuesDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
self.title = TGLocalized(@"Map.ChooseLocationTitle");
|
|
[self setLeftBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:TGLocalized(@"Common.Cancel") style:UIBarButtonItemStylePlain target:self action:@selector(cancelButtonPressed)]];
|
|
|
|
_searchButtonItem = [self controllerRightBarButtonItem];
|
|
_searchButtonItem.enabled = false;
|
|
[self setRightBarButtonItem:_searchButtonItem];
|
|
|
|
[TGLocationUtils requestWhenInUserLocationAuthorizationWithLocationManager:_locationManager];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[_locationUpdateDisposable dispose];
|
|
[_nearbyVenuesDisposable dispose];
|
|
[_searchDisposable dispose];
|
|
|
|
_mapView.delegate = nil;
|
|
_searchBar.delegate = nil;
|
|
_searchMixin.delegate = nil;
|
|
|
|
_nearbyVenuesTableView.dataSource = nil;
|
|
_nearbyVenuesTableView.delegate = nil;
|
|
}
|
|
|
|
- (void)loadView
|
|
{
|
|
[super loadView];
|
|
|
|
self.view.backgroundColor = [UIColor whiteColor];
|
|
|
|
_nearbyVenuesTableView = [[UITableView alloc] initWithFrame:self.view.bounds];
|
|
_nearbyVenuesTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
_nearbyVenuesTableView.dataSource = self;
|
|
_nearbyVenuesTableView.delegate = self;
|
|
_nearbyVenuesTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
|
[self.view addSubview:_nearbyVenuesTableView];
|
|
|
|
//if ([_nearbyVenuesTableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)])
|
|
// _nearbyVenuesTableView.cellLayoutMarginsFollowReadableWidth = false;
|
|
|
|
if (TGLocationPickerPlacesProvider == TGLocationPlacesServiceFoursquare)
|
|
{
|
|
_attributionView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, _nearbyVenuesTableView.frame.size.width, 55)];
|
|
_attributionView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_attributionView.contentMode = UIViewContentModeCenter;
|
|
_attributionView.hidden = true;
|
|
_attributionView.image = TGComponentsImageNamed(@"FoursquareAttribution.png");
|
|
_nearbyVenuesTableView.tableFooterView = _attributionView;
|
|
}
|
|
|
|
_activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
|
_activityIndicator.userInteractionEnabled = false;
|
|
[_nearbyVenuesTableView addSubview:_activityIndicator];
|
|
[_activityIndicator startAnimating];
|
|
|
|
_messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
|
|
_messageLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_messageLabel.backgroundColor = [UIColor clearColor];
|
|
_messageLabel.font = TGSystemFontOfSize(16);
|
|
_messageLabel.hidden = true;
|
|
_messageLabel.textAlignment = NSTextAlignmentCenter;
|
|
_messageLabel.textColor = UIColorRGB(0x8e8e93);
|
|
_messageLabel.userInteractionEnabled = false;
|
|
[_nearbyVenuesTableView addSubview:_messageLabel];
|
|
|
|
CGFloat mapHeight = [TGLocationPickerController mapHeight];
|
|
|
|
CGFloat stripeThickness = TGScreenPixel;
|
|
_tableViewTopInset = mapHeight + stripeThickness;
|
|
|
|
_mapClipView = [[UIView alloc] initWithFrame:CGRectMake(0, -TGLocationPickerMapClipHeight, self.view.frame.size.width, TGLocationPickerMapClipHeight)];
|
|
_mapClipView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_mapClipView.clipsToBounds = true;
|
|
[_nearbyVenuesTableView addSubview:_mapClipView];
|
|
|
|
_mapViewWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, TGLocationPickerMapClipHeight - mapHeight - stripeThickness, self.view.frame.size.width, mapHeight + stripeThickness)];
|
|
_mapViewWrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
[_mapClipView addSubview:_mapViewWrapper];
|
|
|
|
__weak TGLocationPickerController *weakSelf = self;
|
|
|
|
_mapView = [[TGLocationMapView alloc] initWithFrame:CGRectMake(0, -TGLocationPickerMapInset, self.view.frame.size.width, mapHeight + 2 * TGLocationPickerMapInset)];
|
|
_mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_mapView.delegate = self;
|
|
_mapView.showsUserLocation = true;
|
|
_mapView.tapEnabled = true;
|
|
_mapView.longPressAsTapEnabled = true;
|
|
_mapView.singleTap = ^
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
[strongSelf switchToFullscreenMapAnimated:true];
|
|
};
|
|
[_mapViewWrapper addSubview:_mapView];
|
|
|
|
CGFloat pinWrapperWidth = self.view.frame.size.width;
|
|
_pickerPinWrapper = [[TGLocationPinWrapperView alloc] initWithFrame:CGRectMake((_mapViewWrapper.frame.size.width - pinWrapperWidth) / 2, (_mapViewWrapper.frame.size.height - pinWrapperWidth) / 2, pinWrapperWidth, pinWrapperWidth)];
|
|
_pickerPinWrapper.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
|
|
_pickerPinWrapper.hidden = true;
|
|
[_mapViewWrapper addSubview:_pickerPinWrapper];
|
|
|
|
_pickerPinView = [[TGLocationPinView alloc] init];
|
|
_pickerPinView.frame = CGRectMake((_pickerPinWrapper.frame.size.width - _pickerPinView.frame.size.width) / 2, (_pickerPinWrapper.frame.size.height - _pickerPinView.frame.size.height) / 2 - 15, _pickerPinView.frame.size.width, _pickerPinView.frame.size.height);
|
|
[_pickerPinWrapper addSubview:_pickerPinView];
|
|
|
|
_pickerAnnotationView = [[TGPickPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:nil];
|
|
_pickerAnnotationView.calloutPressed = ^
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf _sendLocation];
|
|
};
|
|
_pickerAnnotationView.hidden = true;
|
|
|
|
UIView *stripeView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, _mapViewWrapper.frame.size.height - stripeThickness, _mapViewWrapper.frame.size.width, stripeThickness)];
|
|
stripeView.backgroundColor = TGSeparatorColor();
|
|
stripeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
|
|
[_mapViewWrapper addSubview:stripeView];
|
|
|
|
_searchBarOverlay = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.navigationController.view.frame.size.width, 64)];
|
|
_searchBarOverlay.alpha = 0.0f;
|
|
_searchBarOverlay.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_searchBarOverlay.backgroundColor = UIColorRGB(0xf7f7f7);
|
|
_searchBarOverlay.userInteractionEnabled = false;
|
|
[self.navigationController.view addSubview:_searchBarOverlay];
|
|
|
|
_searchBarWrapper = [[UIView alloc] initWithFrame:CGRectMake(0, -64, self.navigationController.view.frame.size.width, 64)];
|
|
_searchBarWrapper.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_searchBarWrapper.backgroundColor = [UIColor whiteColor];
|
|
_searchBarWrapper.hidden = true;
|
|
[self.navigationController.view addSubview:_searchBarWrapper];
|
|
|
|
_searchBar = [[TGSearchBar alloc] initWithFrame:CGRectMake(0.0f, 20, _searchBarWrapper.frame.size.width, [TGSearchBar searchBarBaseHeight]) style:TGSearchBarStyleHeader];
|
|
_searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_searchBar.customBackgroundView.image = nil;
|
|
_searchBar.customActiveBackgroundView.image = nil;
|
|
_searchBar.delegate = self;
|
|
[_searchBar setShowsCancelButton:true animated:false];
|
|
[_searchBar setAlwaysExtended:true];
|
|
_searchBar.placeholder = TGLocalized(@"Map.Search");
|
|
[_searchBar sizeToFit];
|
|
_searchBar.delayActivity = false;
|
|
[_searchBarWrapper addSubview:_searchBar];
|
|
|
|
_searchMixin = [[TGSearchDisplayMixin alloc] init];
|
|
_searchMixin.searchBar = _searchBar;
|
|
_searchMixin.alwaysShowsCancelButton = true;
|
|
_searchMixin.delegate = self;
|
|
|
|
_toolbarWrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 44.0f, self.view.frame.size.width, 44.0f)];
|
|
_toolbarWrapperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
|
|
_toolbarWrapperView.userInteractionEnabled = false;
|
|
[self.view addSubview:_toolbarWrapperView];
|
|
|
|
_toolbarView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, _toolbarWrapperView.frame.size.height, _toolbarWrapperView.frame.size.width, 44.0f)];
|
|
_toolbarView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_toolbarView.backgroundColor = UIColorRGB(0xf7f7f7);
|
|
[_toolbarWrapperView addSubview:_toolbarView];
|
|
stripeView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, _toolbarView.frame.size.width, stripeThickness)];
|
|
stripeView.backgroundColor = UIColorRGB(0xb2b2b2);
|
|
stripeView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
[_toolbarView addSubview:stripeView];
|
|
|
|
_userLocationButton = [[TGModernButton alloc] initWithFrame:CGRectMake(4, 2, 44, 44)];
|
|
_userLocationButton.adjustsImageWhenHighlighted = false;
|
|
_userLocationButton.contentMode = UIViewContentModeCenter;
|
|
_userLocationButton.exclusiveTouch = true;
|
|
_userLocationButton.enabled = false;
|
|
[_userLocationButton setImage:TGComponentsImageNamed(@"TrackingLocation.png") forState:UIControlStateNormal];
|
|
[_userLocationButton addTarget:self action:@selector(userLocationButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
|
[_toolbarView addSubview:_userLocationButton];
|
|
|
|
_showPlacesButton = [[TGModernButton alloc] initWithFrame:CGRectZero];
|
|
_showPlacesButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
|
|
_showPlacesButton.exclusiveTouch = true;
|
|
_showPlacesButton.titleLabel.font = TGSystemFontOfSize(18);
|
|
[_showPlacesButton setTitle:TGLocalized(@"Map.ShowPlaces") forState:UIControlStateNormal];
|
|
[_showPlacesButton setTitleColor:TGAccentColor()];
|
|
[_showPlacesButton addTarget:self action:@selector(showPlacesButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
|
[_showPlacesButton sizeToFit];
|
|
CGFloat showPlacesWidth = MAX(110, _showPlacesButton.frame.size.width);
|
|
_showPlacesButton.frame = CGRectMake((self.view.frame.size.width - showPlacesWidth) / 2, 0, showPlacesWidth, 44);
|
|
[_toolbarView addSubview:_showPlacesButton];
|
|
|
|
static UIImage *maskImage = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
UIGraphicsBeginImageContextWithOptions(CGSizeMake(55.0f, 43.0f), false, 0.0f);
|
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
|
|
CGColorRef colors[3] = {
|
|
CGColorRetain(UIColorRGBA(0xf7f7f7, 0.0f).CGColor),
|
|
CGColorRetain(UIColorRGBA(0xf7f7f7, 1.0f).CGColor),
|
|
CGColorRetain(UIColorRGBA(0xf7f7f7, 1.0f).CGColor)
|
|
};
|
|
|
|
CFArrayRef colorsArray = CFArrayCreate(kCFAllocatorDefault, (const void **)&colors, 3, NULL);
|
|
CGFloat locations[3] = {0.0f, 0.45f, 1.0f};
|
|
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colorsArray, (CGFloat const *)&locations);
|
|
|
|
CFRelease(colorsArray);
|
|
CFRelease(colors[0]);
|
|
CFRelease(colors[1]);
|
|
CFRelease(colors[2]);
|
|
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
CGContextDrawLinearGradient(context, gradient, CGPointMake(0.0f, 0.0f), CGPointMake(55.0f, 0.0f), 0);
|
|
|
|
CFRelease(gradient);
|
|
|
|
maskImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
});
|
|
|
|
_mapModeControl = [[TGLocationMapModeControl alloc] init];
|
|
_mapModeControl.alpha = 0.0f;
|
|
_mapModeControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
_mapModeControl.userInteractionEnabled = false;
|
|
_mapModeControl.frame = CGRectMake(_toolbarView.frame.size.width, (_toolbarView.frame.size.height - 29) / 2 + 0.5f, _toolbarView.frame.size.width - 55 - 7.5f, 29);
|
|
_mapModeControl.selectedSegmentIndex = MAX(0, MIN(2, (NSInteger)_mapView.mapType));
|
|
[_mapModeControl addTarget:self action:@selector(mapModeControlValueChanged:) forControlEvents:UIControlEventValueChanged];
|
|
[_toolbarView addSubview:_mapModeControl];
|
|
|
|
_mapModeControlMask = [[UIImageView alloc] initWithFrame:CGRectMake(_toolbarView.frame.size.width - 55, 1, 55, 43)];
|
|
_mapModeControlMask.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
|
|
_mapModeControlMask.image = maskImage;
|
|
[_toolbarView addSubview:_mapModeControlMask];
|
|
|
|
_mapModeButton = [[TGModernButton alloc] initWithFrame:CGRectMake(_toolbarView.frame.size.width - 50, TGRetinaPixel, 44, 44)];
|
|
_mapModeButton.adjustsImageWhenHighlighted = false;
|
|
_mapModeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
|
|
_mapModeButton.contentMode = UIViewContentModeCenter;
|
|
_mapModeButton.exclusiveTouch = true;
|
|
[_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo.png") forState:UIControlStateNormal];
|
|
[_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo_Active.png") forState:UIControlStateSelected];
|
|
[_mapModeButton setImage:TGComponentsImageNamed(@"LocationInfo_Active.png") forState:UIControlStateSelected | UIControlStateHighlighted];
|
|
[_mapModeButton addTarget:self action:@selector(mapModeButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
|
[_toolbarView addSubview:_mapModeButton];
|
|
|
|
_searchReferenceView = [[UIView alloc] initWithFrame:self.view.bounds];
|
|
_searchReferenceView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
_searchReferenceView.userInteractionEnabled = false;
|
|
[self.view addSubview:_searchReferenceView];
|
|
|
|
self.scrollViewsForAutomaticInsetsAdjustment = @[ _nearbyVenuesTableView ];
|
|
|
|
if (![self _updateControllerInset:false])
|
|
[self controllerInsetUpdated:UIEdgeInsetsZero];
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated
|
|
{
|
|
[super viewWillAppear:animated];
|
|
|
|
if (_intent == TGLocationPickerControllerCustomLocationIntent)
|
|
{
|
|
[self switchToFullscreenMapAnimated:true];
|
|
_showPlacesButton.hidden = true;
|
|
}
|
|
|
|
__weak TGLocationPickerController *weakSelf = self;
|
|
[_locationUpdateDisposable setDisposable:[[self userLocationSignal] startWithNext:^(TGLocationPair *locationPair)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
[strongSelf setCurrentUserLocation:locationPair.location storeLocation:locationPair.isCurrent updateMapView:!locationPair.onlyLocationUpdate];
|
|
|
|
if (strongSelf->_intent != TGLocationPickerControllerCustomLocationIntent && locationPair.isCurrent && !locationPair.onlyLocationUpdate)
|
|
[strongSelf fetchNearbyVenuesWithLocation:locationPair.location];
|
|
}]];
|
|
|
|
[self _layoutTableProgressViews];
|
|
}
|
|
|
|
- (void)reloadNearbyVenuesIfNeeded
|
|
{
|
|
if (!_nearbyVenuesLoadFailed)
|
|
return;
|
|
|
|
_nearbyVenuesLoadFailed = false;
|
|
[self fetchNearbyVenuesWithLocation:_currentUserLocation];
|
|
}
|
|
|
|
- (void)fetchNearbyVenuesWithLocation:(CLLocation *)location
|
|
{
|
|
_venuesFetchLocation = location;
|
|
|
|
_messageLabel.hidden = true;
|
|
|
|
__weak TGLocationPickerController *weakSelf = self;
|
|
[_nearbyVenuesDisposable setDisposable:[[[TGLocationSignals searchNearbyPlacesWithQuery:nil coordinate:location.coordinate service:TGLocationPickerPlacesProvider] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *venues)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf != nil && venues != nil)
|
|
[strongSelf setNearbyVenues:venues];
|
|
} error:^(__unused id error)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
strongSelf->_nearbyVenuesLoadFailed = true;
|
|
|
|
[strongSelf setIsLoading:false];
|
|
|
|
[strongSelf _layoutTableProgressViews];
|
|
strongSelf->_messageLabel.hidden = false;
|
|
strongSelf->_messageLabel.text = TGLocalized(@"Map.LoadError");
|
|
} completed:nil]];
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated
|
|
{
|
|
[super viewDidAppear:animated];
|
|
|
|
_searchBarWrapper.hidden = false;
|
|
|
|
if (_intent != TGLocationPickerControllerCustomLocationIntent)
|
|
{
|
|
[[[LegacyComponentsGlobals provider] accessChecker] checkLocationAuthorizationStatusForIntent:TGLocationAccessIntentSend alertDismissComlpetion:^
|
|
{
|
|
if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse)
|
|
{
|
|
[self switchToFullscreenMapAnimated:true];
|
|
_showPlacesButton.hidden = true;
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated
|
|
{
|
|
[super viewWillDisappear:animated];
|
|
|
|
_searchBarWrapper.hidden = true;
|
|
}
|
|
|
|
#pragma mark - Actions
|
|
|
|
- (void)cancelButtonPressed
|
|
{
|
|
[self.presentingViewController dismissViewControllerAnimated:true completion:nil];
|
|
}
|
|
|
|
- (void)_sendLocation
|
|
{
|
|
CLLocationCoordinate2D coordinate = _currentUserLocation.coordinate;
|
|
if (_mapInFullScreenMode)
|
|
coordinate = [self mapCenterCoordinateForPickerPin];
|
|
|
|
if (self.locationPicked != nil)
|
|
self.locationPicked(coordinate, nil);
|
|
}
|
|
|
|
- (void)searchButtonPressed
|
|
{
|
|
[self setSearchHidden:false animated:true];
|
|
[_searchBar becomeFirstResponder];
|
|
}
|
|
|
|
- (void)showPlacesButtonPressed
|
|
{
|
|
[self switchToVenuesTableView];
|
|
}
|
|
|
|
- (void)userLocationButtonPressed
|
|
{
|
|
if (!_pinMovedFromUserLocation || _currentUserLocation == nil)
|
|
return;
|
|
|
|
_pinMovedFromUserLocation = false;
|
|
|
|
[self hidePickerAnnotationAnimated:true];
|
|
[_pickerPinView setPinRaised:true animated:true completion:nil];
|
|
|
|
MKCoordinateSpan span = _fullScreenMapSpan != nil ? _fullScreenMapSpan.MKCoordinateSpanValue : TGLocationDefaultSpan;
|
|
[self _setMapCenterCoordinate:_mapView.userLocation.location.coordinate span:span offset:TGLocationPickerPinOffset animated:true];
|
|
}
|
|
|
|
- (void)mapModeButtonPressed
|
|
{
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismissMapModeControl) object:nil];
|
|
[self setMapModeControlHidden:_mapModeButton.selected withUserAction:true animated:true];
|
|
}
|
|
|
|
- (void)mapModeControlValueChanged:(TGLocationMapModeControl *)sender
|
|
{
|
|
NSInteger mapMode = MAX(0, MIN(2, sender.selectedSegmentIndex));
|
|
[_mapView setMapType:(MKMapType)mapMode];
|
|
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismissMapModeControl) object:nil];
|
|
if (_mapModeButton.isSelected)
|
|
[self performSelector:@selector(dismissMapModeControl) withObject:nil afterDelay:1.0f];
|
|
}
|
|
|
|
- (void)dismissMapModeControl
|
|
{
|
|
[self setMapModeControlHidden:true withUserAction:false animated:true];
|
|
}
|
|
|
|
#pragma mark - Map View Delegate
|
|
|
|
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)__unused animated
|
|
{
|
|
UIView *view = mapView.subviews.firstObject;
|
|
|
|
for (UIGestureRecognizer *recognizer in view.gestureRecognizers)
|
|
{
|
|
if(recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateEnded)
|
|
{
|
|
if (_mapInFullScreenMode)
|
|
{
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(pinPinView) object:nil];
|
|
[self hidePickerAnnotationAnimated:true];
|
|
}
|
|
else
|
|
{
|
|
[self switchToFullscreenMapAnimated:true];
|
|
}
|
|
|
|
[_pickerPinView setPinRaised:true animated:true completion:nil];
|
|
|
|
_pinMovedFromUserLocation = true;
|
|
_updatePinAnnotation = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)mapView:(MKMapView *)__unused mapView regionDidChangeAnimated:(BOOL)__unused animated
|
|
{
|
|
if (_pickerPinView.isPinRaised)
|
|
{
|
|
NSTimeInterval delay = _pinMovedFromUserLocation ? 0.38 : 0.05;
|
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(pinPinView) object:nil];
|
|
[self performSelector:@selector(pinPinView) withObject:nil afterDelay:delay];
|
|
}
|
|
else if (_updatePinAnnotation)
|
|
{
|
|
[self pinPinView];
|
|
}
|
|
}
|
|
|
|
- (void)pinPinView
|
|
{
|
|
__weak TGLocationPickerController *weakSelf = self;
|
|
[_pickerPinView setPinRaised:false animated:true completion:^
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf showPickerAnnotationAnimated:true];
|
|
}];
|
|
}
|
|
|
|
- (void)mapView:(MKMapView *)__unused mapView didUpdateUserLocation:(MKUserLocation *)userLocation
|
|
{
|
|
userLocation.title = @"";
|
|
|
|
_locationServicesDisabled = false;
|
|
|
|
if (_userLocationObserver != nil)
|
|
_userLocationObserver(userLocation.location);
|
|
else if (userLocation.location != nil)
|
|
_startLocation = userLocation.location;
|
|
}
|
|
|
|
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
|
|
{
|
|
if (annotation == mapView.userLocation)
|
|
return nil;
|
|
|
|
MKPinAnnotationView *view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:TGPickPinAnnotationKind];
|
|
if (view == nil)
|
|
view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:TGPickPinAnnotationKind];
|
|
else
|
|
view.annotation = annotation;
|
|
|
|
if (_mapInFullScreenMode)
|
|
view.hidden = true;
|
|
|
|
view.canShowCallout = false;
|
|
view.animatesDrop = false;
|
|
view.draggable = false;
|
|
|
|
return view;
|
|
}
|
|
|
|
- (void)updateAnnotationWithLocation:(CLLocation *)location
|
|
{
|
|
if (_mapView.userLocation == nil || _mapView.userLocation.location == nil)
|
|
return;
|
|
|
|
if (_annotation == nil)
|
|
{
|
|
_annotation = [[TGLocationAnnotation alloc] initWithCoordinate:_currentUserLocation.coordinate title:nil];
|
|
[_mapView addAnnotation:_annotation];
|
|
[_mapView selectAnnotation:_annotation animated:false];
|
|
}
|
|
|
|
_annotation.coordinate = location.coordinate;
|
|
}
|
|
|
|
- (void)updatePickerAnnotation
|
|
{
|
|
__weak TGLocationPickerController *weakSelf = self;
|
|
|
|
CLLocationCoordinate2D coordinate = [self mapCenterCoordinateForPickerPin];
|
|
_customAnnotation = [[TGLocationAnnotation alloc] initWithCoordinate:coordinate title:TGLocalized(@"Map.SendThisLocation")];
|
|
[self _updatePickerAnnotationViewAnimated:false];
|
|
|
|
[_reverseGeocodeDisposable setDisposable:[[[TGLocationSignals reverseGeocodeCoordinate:coordinate] deliverOn:[SQueue mainQueue]] startWithNext:^(TGLocationReverseGeocodeResult *result)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
NSString *address = @"";
|
|
if (result != nil)
|
|
address = result.displayAddress;
|
|
|
|
strongSelf->_customAnnotation.subtitle = address;
|
|
[strongSelf updateCurrentLocationCell];
|
|
} error:^(__unused id error)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return;
|
|
|
|
strongSelf->_customAnnotation.subtitle = TGLocalized(@"Map.LocatingError");
|
|
[strongSelf updateCurrentLocationCell];
|
|
} completed:^
|
|
{
|
|
|
|
}]];
|
|
}
|
|
|
|
- (void)showPickerAnnotationAnimated:(bool)__unused animated
|
|
{
|
|
[self updatePickerAnnotation];
|
|
_customAnnotation.subtitle = nil;
|
|
|
|
[self updateCurrentLocationCell];
|
|
}
|
|
|
|
- (void)hidePickerAnnotationAnimated:(bool)__unused animated
|
|
{
|
|
_customAnnotation.subtitle = nil;
|
|
[self updateCurrentLocationCell];
|
|
}
|
|
|
|
- (void)_updatePickerAnnotationViewAnimated:(bool)animated
|
|
{
|
|
TGPickPinAnnotationView *annotationView = _pickerAnnotationView;
|
|
annotationView.annotation = _customAnnotation;
|
|
[annotationView sizeToFit];
|
|
[annotationView setNeedsLayout];
|
|
|
|
if (animated && annotationView.appeared)
|
|
{
|
|
[UIView animateWithDuration:0.2f animations:^
|
|
{
|
|
[annotationView layoutIfNeeded];
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (CLLocationCoordinate2D)mapCenterCoordinateForPickerPin
|
|
{
|
|
return [_mapView convertPoint:CGPointMake((_mapView.frame.size.width + TGLocationPickerPinOffset.x) / 2, (_mapView.frame.size.height + TGLocationPickerPinOffset.y) / 2) toCoordinateFromView:_mapView];
|
|
}
|
|
|
|
- (void)_setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate offset:(CGPoint)offset animated:(bool)animated
|
|
{
|
|
[self _setMapCenterCoordinate:coordinate span:TGLocationDefaultSpan offset:offset animated:animated];
|
|
}
|
|
|
|
- (void)_setMapCenterCoordinate:(CLLocationCoordinate2D)coordinate span:(MKCoordinateSpan)span offset:(CGPoint)offset animated:(bool)animated
|
|
{
|
|
@try
|
|
{
|
|
MKCoordinateRegion region = MKCoordinateRegionMake(coordinate, span);
|
|
if (!CGPointEqualToPoint(offset, CGPointZero))
|
|
{
|
|
MKMapRect mapRect = [TGLocationUtils MKMapRectForCoordinateRegion:region];
|
|
[_mapView setVisibleMapRect:mapRect edgePadding:UIEdgeInsetsMake(offset.y, offset.x, 0, 0) animated:animated];
|
|
}
|
|
else
|
|
{
|
|
[_mapView setRegion:region animated:animated];
|
|
}
|
|
}
|
|
@catch (NSException *exception)
|
|
{
|
|
TGLegacyLog(@"ERROR: failed to set location picker map region with exception: %@", exception);
|
|
}
|
|
}
|
|
|
|
#pragma mark - Signals
|
|
|
|
- (SSignal *)userLocationSignal
|
|
{
|
|
__weak TGLocationPickerController *weakSelf = self;
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
|
|
|
|
[disposable setDisposable:[[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf == nil)
|
|
return nil;
|
|
|
|
if (strongSelf->_startLocation != nil)
|
|
{
|
|
[subscriber putNext:[TGLocationPair pairWithLocation:strongSelf->_startLocation isCurrent:true onlyLocationUpdate:false]];
|
|
strongSelf->_startLocation = nil;
|
|
}
|
|
else
|
|
{
|
|
CLLocation *knownUserLocation = [TGLocationSignals lastKnownUserLocation];
|
|
[subscriber putNext:[TGLocationPair pairWithLocation:knownUserLocation isCurrent:false onlyLocationUpdate:false]];
|
|
}
|
|
|
|
strongSelf->_userLocationObserver = ^(CLLocation *location)
|
|
{
|
|
[subscriber putNext:[TGLocationPair pairWithLocation:location isCurrent:true onlyLocationUpdate:false]];
|
|
};
|
|
|
|
return nil;
|
|
}] map:^TGLocationPair *(TGLocationPair *locationPair)
|
|
{
|
|
if (!locationPair.isCurrent)
|
|
return locationPair;
|
|
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
CLLocation *location = locationPair.location;
|
|
|
|
if (strongSelf != nil && strongSelf->_venuesFetchLocation != nil)
|
|
{
|
|
CLLocation *currentLocation = strongSelf->_venuesFetchLocation;
|
|
if ([location distanceFromLocation:currentLocation] < 250)
|
|
{
|
|
if ((location.horizontalAccuracy < currentLocation.horizontalAccuracy || fabs(location.horizontalAccuracy - currentLocation.horizontalAccuracy) < 50))
|
|
{
|
|
locationPair.onlyLocationUpdate = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return locationPair;
|
|
}] startWithNext:^(TGLocationPair *locationPair)
|
|
{
|
|
[subscriber putNext:locationPair];
|
|
}]];
|
|
|
|
return disposable;
|
|
}];
|
|
}
|
|
|
|
- (void)setCurrentUserLocation:(CLLocation *)userLocation storeLocation:(bool)storeLocation updateMapView:(bool)updateMapView
|
|
{
|
|
if (userLocation == nil)
|
|
return;
|
|
|
|
bool hadNoLocation = (_currentUserLocation == nil);
|
|
|
|
if (_mapInFullScreenMode && hadNoLocation)
|
|
_pinMovedFromUserLocation = true;
|
|
|
|
if (storeLocation)
|
|
{
|
|
[TGLocationSignals storeLastKnownUserLocation:userLocation];
|
|
_currentUserLocation = userLocation;
|
|
_searchButtonItem.enabled = true;
|
|
_userLocationButton.enabled = true;
|
|
|
|
if (updateMapView)
|
|
[self updateAnnotationWithLocation:_currentUserLocation];
|
|
}
|
|
|
|
[self updateCurrentLocationCell];
|
|
|
|
if (updateMapView)
|
|
{
|
|
if (!_mapInFullScreenMode)
|
|
{
|
|
[self _setMapCenterCoordinate:userLocation.coordinate offset:TGLocationPickerPinOffset animated:true];
|
|
}
|
|
else if (_intent == TGLocationPickerControllerCustomLocationIntent && hadNoLocation)
|
|
{
|
|
_pinMovedFromUserLocation = false;
|
|
_updatePinAnnotation = true;
|
|
[self _setMapCenterCoordinate:userLocation.coordinate offset:TGLocationPickerPinOffset animated:true];
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark - Appearance
|
|
|
|
- (void)switchToFullscreenMapAnimated:(bool)__unused animated
|
|
{
|
|
_mapInFullScreenMode = true;
|
|
|
|
_searchButtonItem.enabled = true;
|
|
|
|
MKAnnotationView *annotationView = [_mapView viewForAnnotation:_annotation];
|
|
annotationView.hidden = true;
|
|
_pickerPinWrapper.hidden = false;
|
|
|
|
[self showPickerAnnotationAnimated:true];
|
|
|
|
_mapView.tapEnabled = false;
|
|
_mapView.longPressAsTapEnabled = false;
|
|
|
|
[self setToolbarHidden:false animated:true];
|
|
|
|
_nearbyVenuesTableView.clipsToBounds = false;
|
|
_nearbyVenuesTableView.scrollEnabled = false;
|
|
[_mapViewWrapper.superview bringSubviewToFront:_mapViewWrapper];
|
|
|
|
void (^changeBlock)(void) = ^
|
|
{
|
|
CGFloat toolbarHeight = _toolbarView.frame.size.height;
|
|
|
|
_nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top);
|
|
_nearbyVenuesTableView.frame = CGRectMake(_nearbyVenuesTableView.frame.origin.x, self.view.frame.size.height - [TGLocationPickerController mapHeight] - TGLocationCurrentLocationCellHeight - self.controllerInset.top - toolbarHeight, _nearbyVenuesTableView.frame.size.width, _nearbyVenuesTableView.frame.size.height);
|
|
|
|
_mapViewWrapper.frame = CGRectMake(0, TGLocationPickerMapClipHeight - self.view.frame.size.height + self.controllerInset.top + toolbarHeight + 20, _mapViewWrapper.frame.size.width, self.view.frame.size.height - TGLocationCurrentLocationCellHeight - self.controllerInset.top - 7);
|
|
_mapView.center = CGPointMake(_mapView.center.x, _mapViewWrapper.frame.size.height / 2);
|
|
};
|
|
|
|
void (^completionBlock)(BOOL) = ^(BOOL finished)
|
|
{
|
|
if (finished)
|
|
{
|
|
_nearbyVenuesTableView.clipsToBounds = true;
|
|
_mapView.manipulationEnabled = true;
|
|
|
|
_mapViewWrapper.frame = [self.view convertRect:_mapViewWrapper.frame fromView:_mapViewWrapper.superview];
|
|
[self.view insertSubview:_mapViewWrapper belowSubview:_toolbarWrapperView];
|
|
_mapViewWrapper.clipsToBounds = true;
|
|
|
|
if (_annotation != nil)
|
|
_fullScreenMapSpan = [NSValue valueWithMKCoordinateSpan:_mapView.region.span];
|
|
}
|
|
};
|
|
|
|
if (animated)
|
|
{
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.75f initialSpringVelocity:0.5f options:UIViewAnimationOptionCurveLinear animations:changeBlock completion:completionBlock];
|
|
}
|
|
else
|
|
{
|
|
[UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:changeBlock completion:completionBlock];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
changeBlock();
|
|
completionBlock(true);
|
|
}
|
|
}
|
|
|
|
- (void)switchToVenuesTableView
|
|
{
|
|
_mapInFullScreenMode = false;
|
|
|
|
_searchButtonItem.enabled = (_currentUserLocation != nil);
|
|
|
|
MKAnnotationView *annotationView = [_mapView viewForAnnotation:_annotation];
|
|
annotationView.hidden = false;
|
|
_pickerPinWrapper.hidden = true;
|
|
|
|
[self updateCurrentLocationCell];
|
|
|
|
_mapView.mapType = MKMapTypeStandard;
|
|
|
|
[self controllerInsetUpdated:self.controllerInset];
|
|
|
|
[self setToolbarHidden:true animated:true];
|
|
if (_annotation != nil)
|
|
[self _setMapCenterCoordinate:_annotation.coordinate offset:TGLocationPickerPinOffset animated:true];
|
|
|
|
_nearbyVenuesTableView.clipsToBounds = false;
|
|
|
|
_mapViewWrapper.frame = CGRectMake(0, TGLocationPickerMapClipHeight - self.view.frame.size.height + self.controllerInset.top + _toolbarView.frame.size.height + 20, _mapViewWrapper.frame.size.width, self.view.frame.size.height - TGLocationCurrentLocationCellHeight - self.controllerInset.top - 7);
|
|
[_mapClipView addSubview:_mapViewWrapper];
|
|
_mapViewWrapper.clipsToBounds = false;
|
|
|
|
void (^animationBlock)(void) = ^
|
|
{
|
|
_nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top);
|
|
_nearbyVenuesTableView.frame = CGRectMake(_nearbyVenuesTableView.frame.origin.x, 0, _nearbyVenuesTableView.frame.size.width, _nearbyVenuesTableView.frame.size.height);
|
|
|
|
CGFloat stripeThickness = TGScreenPixel;
|
|
_mapViewWrapper.frame = CGRectMake(0, TGLocationPickerMapClipHeight - [TGLocationPickerController mapHeight] - stripeThickness, self.view.frame.size.width, [TGLocationPickerController mapHeight] + stripeThickness);
|
|
_mapView.frame = CGRectMake(_mapView.frame.origin.x, (_mapViewWrapper.frame.size.height - _mapView.frame.size.height) / 2, _mapView.frame.size.width, _mapView.frame.size.height);
|
|
};
|
|
|
|
void (^completionBlock)(BOOL) = ^(BOOL finished)
|
|
{
|
|
_mapModeControl.selectedSegmentIndex = 0;
|
|
if (finished)
|
|
{
|
|
_mapView.tapEnabled = true;
|
|
_mapView.longPressAsTapEnabled = true;
|
|
_nearbyVenuesTableView.clipsToBounds = true;
|
|
_nearbyVenuesTableView.scrollEnabled = true;
|
|
}
|
|
};
|
|
|
|
if (iosMajorVersion() >= 7)
|
|
{
|
|
[UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:0.85f initialSpringVelocity:0.5f options:UIViewAnimationOptionCurveLinear animations:animationBlock completion:completionBlock];
|
|
}
|
|
else
|
|
{
|
|
[UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:animationBlock completion:completionBlock];
|
|
}
|
|
|
|
[self reloadNearbyVenuesIfNeeded];
|
|
}
|
|
|
|
- (void)setToolbarHidden:(bool)hidden animated:(bool)animated
|
|
{
|
|
_toolbarWrapperView.userInteractionEnabled = !hidden;
|
|
|
|
void (^changeBlock)(void) = ^
|
|
{
|
|
_toolbarView.frame = CGRectMake(_toolbarView.frame.origin.x, hidden ? _toolbarWrapperView.frame.size.height : 0, _toolbarView.frame.size.width, _toolbarView.frame.size.height);
|
|
};
|
|
|
|
if (animated)
|
|
[UIView animateWithDuration:0.3f delay:0.0f options:[TGViewController preferredAnimationCurve] << 16 animations:changeBlock completion:nil];
|
|
else
|
|
changeBlock();
|
|
}
|
|
|
|
- (void)setMapModeControlHidden:(bool)hidden withUserAction:(bool)withUserAction animated:(bool)animated
|
|
{
|
|
_userLocationButton.userInteractionEnabled = hidden;
|
|
_showPlacesButton.userInteractionEnabled = hidden;
|
|
_mapModeControl.userInteractionEnabled = !hidden;
|
|
_mapModeButton.userInteractionEnabled = true;
|
|
|
|
if (!withUserAction && animated)
|
|
{
|
|
[UIView transitionWithView:_mapModeButton duration:0.25f options:UIViewAnimationOptionTransitionCrossDissolve animations:^
|
|
{
|
|
_mapModeButton.selected = !hidden;
|
|
} completion:nil];
|
|
}
|
|
else
|
|
{
|
|
_mapModeButton.selected = !hidden;
|
|
}
|
|
|
|
if (animated)
|
|
{
|
|
[UIView animateWithDuration:0.3f delay:0.0f options:[TGViewController preferredAnimationCurve] << 16 animations:^
|
|
{
|
|
_mapModeControl.frame = CGRectMake(hidden ? _toolbarView.frame.size.width : 8, _mapModeControl.frame.origin.y, _mapModeControl.frame.size.width, _mapModeControl.frame.size.height);
|
|
} completion:nil];
|
|
|
|
[UIView animateWithDuration:0.25f animations:^
|
|
{
|
|
if (hidden)
|
|
{
|
|
_mapModeControl.alpha = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
_userLocationButton.alpha = 0.0f;
|
|
_showPlacesButton.alpha = 0.0f;
|
|
}
|
|
}];
|
|
|
|
[UIView animateWithDuration:0.25f delay:0.05f options:kNilOptions animations:^
|
|
{
|
|
if (hidden)
|
|
{
|
|
_userLocationButton.alpha = 1.0f;
|
|
_showPlacesButton.alpha = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
_mapModeControl.alpha = 1.0f;
|
|
}
|
|
} completion:nil];
|
|
}
|
|
else
|
|
{
|
|
_userLocationButton.alpha = hidden ? 1.0f : 0.0f;
|
|
_showPlacesButton.alpha = hidden ? 1.0f : 0.0f;
|
|
_mapModeControl.alpha = hidden ? 0.0f : 1.0f;
|
|
}
|
|
}
|
|
|
|
- (UIBarButtonItem *)controllerRightBarButtonItem
|
|
{
|
|
if (iosMajorVersion() < 7)
|
|
{
|
|
TGModernBarButton *searchButton = [[TGModernBarButton alloc] initWithImage:TGComponentsImageNamed(@"NavigationSearchIcon.png")];
|
|
searchButton.portraitAdjustment = CGPointMake(-7, -5);
|
|
[searchButton addTarget:self action:@selector(searchButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
|
return [[UIBarButtonItem alloc] initWithCustomView:searchButton];
|
|
}
|
|
|
|
return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchButtonPressed)];
|
|
}
|
|
|
|
#pragma mark - Search
|
|
|
|
- (void)setSearchHidden:(bool)hidden animated:(bool)animated
|
|
{
|
|
void (^changeBlock)(void) = ^
|
|
{
|
|
CGRect frame = _searchBarWrapper.frame;
|
|
if (hidden)
|
|
{
|
|
frame.origin.y = -64;
|
|
_searchBarOverlay.alpha = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
frame.origin.y = 0;
|
|
if (self.navigationController.modalPresentationStyle == UIModalPresentationFormSheet)
|
|
frame.origin.y -= 20;
|
|
|
|
_searchBarOverlay.alpha = 1.0f;
|
|
}
|
|
_searchBarWrapper.frame = frame;
|
|
};
|
|
|
|
if (animated)
|
|
[UIView animateWithDuration:0.2f animations:changeBlock];
|
|
else
|
|
changeBlock();
|
|
}
|
|
|
|
- (void)searchBar:(TGSearchBar *)__unused searchBar willChangeHeight:(CGFloat)__unused newHeight
|
|
{
|
|
|
|
}
|
|
|
|
- (void)searchMixin:(TGSearchDisplayMixin *)__unused searchMixin hasChangedSearchQuery:(NSString *)searchQuery withScope:(int)__unused scope
|
|
{
|
|
if (searchQuery.length == 0)
|
|
{
|
|
[_searchDisposable setDisposable:nil];
|
|
[_searchMixin reloadSearchResults];
|
|
[_searchMixin setSearchResultsTableViewHidden:true];
|
|
_searchBar.showActivity = false;
|
|
}
|
|
else
|
|
{
|
|
__weak TGLocationPickerController *weakSelf = self;
|
|
void (^changeActivityIndicatorState)(bool) = ^(bool active)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
strongSelf->_searchBar.showActivity = active;
|
|
};
|
|
|
|
SSignal *searchSignal = [[SSignal complete] delay:0.65f onQueue:[SQueue mainQueue]];
|
|
searchSignal = [searchSignal onCompletion:^
|
|
{
|
|
changeActivityIndicatorState(true);
|
|
}];
|
|
|
|
CLLocationCoordinate2D coordinate = _mapInFullScreenMode ? _customAnnotation.coordinate : _currentUserLocation.coordinate;
|
|
searchSignal = [[searchSignal then:[TGLocationSignals searchNearbyPlacesWithQuery:searchQuery coordinate:coordinate service:TGLocationPickerPlacesProvider]] deliverOn:[SQueue mainQueue]];
|
|
|
|
if (_searchDisposable == nil)
|
|
_searchDisposable = [[SMetaDisposable alloc] init];
|
|
|
|
[_searchDisposable setDisposable:[[searchSignal onDispose:^
|
|
{
|
|
changeActivityIndicatorState(false);
|
|
}] startWithNext:^(NSArray *results)
|
|
{
|
|
__strong TGLocationPickerController *strongSelf = weakSelf;
|
|
if (strongSelf != nil)
|
|
[strongSelf setSearchResults:results withSearchQuery:searchQuery];
|
|
} error:^(__unused id error)
|
|
{
|
|
changeActivityIndicatorState(false);
|
|
} completed:^
|
|
{
|
|
changeActivityIndicatorState(false);
|
|
}]];
|
|
}
|
|
}
|
|
|
|
- (void)searchMixinWillActivate:(bool)__unused animated
|
|
{
|
|
if (_mapInFullScreenMode)
|
|
return;
|
|
|
|
_nearbyVenuesTableView.scrollEnabled = false;
|
|
|
|
[UIView animateWithDuration:0.2f animations:^
|
|
{
|
|
_nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top);
|
|
[self _layoutTableProgressViews];
|
|
}];
|
|
}
|
|
|
|
- (void)searchMixinWillDeactivate:(bool)animated
|
|
{
|
|
[_searchDisposable setDisposable:nil];
|
|
|
|
[self setSearchHidden:true animated:animated];
|
|
|
|
if (_mapInFullScreenMode)
|
|
return;
|
|
|
|
_nearbyVenuesTableView.scrollEnabled = true;
|
|
|
|
[UIView animateWithDuration:0.2f animations:^
|
|
{
|
|
_nearbyVenuesTableView.contentOffset = CGPointMake(0, -_nearbyVenuesTableView.contentInset.top);
|
|
[self _layoutTableProgressViews];
|
|
}];
|
|
}
|
|
|
|
- (UITableView *)createTableViewForSearchMixin:(TGSearchDisplayMixin *)__unused searchMixin
|
|
{
|
|
UITableView *tableView = [[UITableView alloc] init];
|
|
tableView.delegate = self;
|
|
tableView.dataSource = self;
|
|
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
|
tableView.tableFooterView = [[UIView alloc] init];
|
|
|
|
return tableView;
|
|
}
|
|
|
|
- (UIView *)referenceViewForSearchResults
|
|
{
|
|
return _searchReferenceView;
|
|
}
|
|
|
|
#pragma mark - Scroll View Delegate
|
|
|
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
|
{
|
|
if (_searchMixin.isActive && scrollView == _searchMixin.searchResultsTableView)
|
|
{
|
|
[_searchBar resignFirstResponder];
|
|
}
|
|
else if (scrollView == _nearbyVenuesTableView)
|
|
{
|
|
[self _layoutTableProgressViews];
|
|
|
|
CGFloat offset = scrollView.contentInset.top + scrollView.contentOffset.y;
|
|
CGFloat mapOffset = MIN(offset, [TGLocationPickerController mapHeight]);
|
|
_mapView.frame = CGRectMake(_mapView.frame.origin.x, -TGLocationPickerMapInset + mapOffset / 2, _mapView.frame.size.width, _mapView.frame.size.height);
|
|
}
|
|
}
|
|
|
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
{
|
|
if (_searchMixin.isActive && scrollView == _searchMixin.searchResultsTableView)
|
|
[_searchBar resignFirstResponder];
|
|
}
|
|
|
|
#pragma mark - Data
|
|
|
|
- (void)updateCurrentLocationCell
|
|
{
|
|
UITableViewCell *cell = [_nearbyVenuesTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
|
|
if ([cell isKindOfClass:[TGLocationCurrentLocationCell class]])
|
|
{
|
|
TGLocationCurrentLocationCell *locationCell = (TGLocationCurrentLocationCell *)cell;
|
|
|
|
if (_mapInFullScreenMode)
|
|
[locationCell configureForCustomLocationWithAddress:_customAnnotation.subtitle];
|
|
else
|
|
[locationCell configureForCurrentLocationWithAccuracy:_currentUserLocation.horizontalAccuracy];
|
|
}
|
|
|
|
[cell.superview bringSubviewToFront:cell];
|
|
}
|
|
|
|
- (void)setNearbyVenues:(NSArray *)nearbyVenues
|
|
{
|
|
bool shouldFadeIn = (_nearbyVenues.count == 0);
|
|
|
|
_nearbyVenues = nearbyVenues;
|
|
[_nearbyVenuesTableView reloadData];
|
|
|
|
[self setIsLoading:false];
|
|
|
|
_attributionView.hidden = (_nearbyVenues.count == 0);
|
|
|
|
if (shouldFadeIn)
|
|
{
|
|
NSMutableArray *animatedCells = [[NSMutableArray alloc] init];
|
|
|
|
for (UIView *cell in _nearbyVenuesTableView.visibleCells)
|
|
{
|
|
if ([cell isKindOfClass:[TGLocationVenueCell class]])
|
|
{
|
|
cell.alpha = 0.0f;
|
|
[animatedCells addObject:cell];
|
|
}
|
|
}
|
|
|
|
[UIView animateWithDuration:0.14f animations:^
|
|
{
|
|
for (UIView *cell in animatedCells)
|
|
cell.alpha = 1.0f;
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (void)setSearchResults:(NSArray *)results withSearchQuery:(NSString *)query
|
|
{
|
|
_searchResults = results;
|
|
_searchResultsQuery = query;
|
|
|
|
[_searchMixin reloadSearchResults];
|
|
[_searchMixin setSearchResultsTableViewHidden:query.length == 0];
|
|
}
|
|
|
|
#pragma mark - Table View Data Source & Delegate
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (tableView == _nearbyVenuesTableView && indexPath.row == 0)
|
|
{
|
|
[self _sendLocation];
|
|
}
|
|
else
|
|
{
|
|
TGLocationVenue *venue = nil;
|
|
if (tableView == _nearbyVenuesTableView)
|
|
{
|
|
venue = _nearbyVenues[indexPath.row - 2];
|
|
}
|
|
else if (tableView == _searchMixin.searchResultsTableView)
|
|
{
|
|
venue = _searchResults[indexPath.row];
|
|
[_searchBar resignFirstResponder];
|
|
}
|
|
|
|
if (self.locationPicked != nil)
|
|
self.locationPicked(venue.coordinate, [venue venueAttachment]);
|
|
}
|
|
}
|
|
|
|
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (tableView == _nearbyVenuesTableView)
|
|
{
|
|
if (indexPath.row == 0)
|
|
return (_mapInFullScreenMode || _currentUserLocation != nil);
|
|
if (indexPath.row == 1)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (tableView == _nearbyVenuesTableView && indexPath.row == 1)
|
|
return nil;
|
|
|
|
return indexPath;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
UITableViewCell *cell = nil;
|
|
|
|
if (tableView == _nearbyVenuesTableView && indexPath.row == 0)
|
|
{
|
|
TGLocationCurrentLocationCell *locationCell = [tableView dequeueReusableCellWithIdentifier:TGLocationCurrentLocationCellKind];
|
|
if (locationCell == nil)
|
|
locationCell = [[TGLocationCurrentLocationCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationCurrentLocationCellKind];
|
|
|
|
if (_mapInFullScreenMode)
|
|
[locationCell configureForCustomLocationWithAddress:_customAnnotation.subtitle];
|
|
else
|
|
[locationCell configureForCurrentLocationWithAccuracy:_currentUserLocation.horizontalAccuracy];
|
|
|
|
cell = locationCell;
|
|
}
|
|
else if (tableView == _nearbyVenuesTableView && indexPath.row == 1)
|
|
{
|
|
TGLocationSectionHeaderCell *sectionCell = [tableView dequeueReusableCellWithIdentifier:TGLocationSectionHeaderKind];
|
|
if (sectionCell == nil)
|
|
sectionCell = [[TGLocationSectionHeaderCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationSectionHeaderKind];
|
|
|
|
[sectionCell configureWithTitle:TGLocalized(@"Map.ChooseAPlace")];
|
|
|
|
cell = sectionCell;
|
|
}
|
|
else
|
|
{
|
|
TGLocationVenueCell *venueCell = [tableView dequeueReusableCellWithIdentifier:TGLocationVenueCellKind];
|
|
if (venueCell == nil)
|
|
venueCell = [[TGLocationVenueCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TGLocationVenueCellKind];
|
|
|
|
TGLocationVenue *venue = nil;
|
|
if (tableView == _nearbyVenuesTableView)
|
|
venue = _nearbyVenues[indexPath.row - 2];
|
|
else if (tableView == _searchMixin.searchResultsTableView)
|
|
venue = _searchResults[indexPath.row];
|
|
|
|
[venueCell configureWithVenue:venue];
|
|
|
|
cell = venueCell;
|
|
}
|
|
|
|
return cell;
|
|
}
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)__unused section
|
|
{
|
|
if (tableView == _nearbyVenuesTableView)
|
|
return _nearbyVenues.count + 2;
|
|
else if (tableView == _searchMixin.searchResultsTableView)
|
|
return _searchResults.count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
- (CGFloat)tableView:(UITableView *)__unused tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (tableView == _nearbyVenuesTableView)
|
|
{
|
|
if (indexPath.row == 0)
|
|
return TGLocationCurrentLocationCellHeight;
|
|
else if (indexPath.row == 1)
|
|
return TGLocationSectionHeaderHeight;
|
|
}
|
|
|
|
return TGLocationVenueCellHeight;
|
|
}
|
|
|
|
- (CGFloat)tableView:(UITableView *)__unused tableView heightForFooterInSection:(NSInteger)__unused section
|
|
{
|
|
return 0.001f;
|
|
}
|
|
|
|
- (UIView *)tableView:(UITableView *)__unused tableView viewForFooterInSection:(NSInteger)__unused section
|
|
{
|
|
return [[UIView alloc] init];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
- (void)setIsLoading:(bool)isLoading
|
|
{
|
|
if (isLoading)
|
|
{
|
|
if (_nearbyVenues.count == 0)
|
|
[_activityIndicator startAnimating];
|
|
}
|
|
else
|
|
{
|
|
[_activityIndicator stopAnimating];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Layout
|
|
|
|
- (BOOL)shouldAutorotate
|
|
{
|
|
return false;
|
|
}
|
|
|
|
- (void)_autoAdjustInsetsForScrollView:(UIScrollView *)scrollView previousInset:(UIEdgeInsets)previousInset
|
|
{
|
|
if (_mapInFullScreenMode)
|
|
return;
|
|
|
|
CGPoint contentOffset = scrollView.contentOffset;
|
|
|
|
UIEdgeInsets finalInset = self.controllerInset;
|
|
finalInset.top += _tableViewTopInset;
|
|
|
|
scrollView.contentInset = finalInset;
|
|
scrollView.scrollIndicatorInsets = self.controllerScrollInset;
|
|
|
|
if (!UIEdgeInsetsEqualToEdgeInsets(previousInset, UIEdgeInsetsZero))
|
|
{
|
|
CGFloat maxOffset = scrollView.contentSize.height - (scrollView.frame.size.height - finalInset.bottom);
|
|
|
|
if (![self shouldAdjustScrollViewInsetsForInversedLayout])
|
|
contentOffset.y += previousInset.top - finalInset.top;
|
|
|
|
contentOffset.y = MAX(-finalInset.top, MIN(contentOffset.y, maxOffset));
|
|
[scrollView setContentOffset:contentOffset animated:false];
|
|
}
|
|
else if (contentOffset.y < finalInset.top)
|
|
{
|
|
contentOffset.y = -finalInset.top;
|
|
[scrollView setContentOffset:contentOffset animated:false];
|
|
}
|
|
}
|
|
|
|
- (void)controllerInsetUpdated:(UIEdgeInsets)previousInset
|
|
{
|
|
if (_searchMixin != nil)
|
|
{
|
|
UIEdgeInsets inset = self.controllerInset;
|
|
inset.top -= 44;
|
|
[_searchMixin controllerInsetUpdated:inset];
|
|
}
|
|
|
|
[super controllerInsetUpdated:previousInset];
|
|
}
|
|
|
|
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
|
|
{
|
|
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
|
|
|
|
if (_searchMixin != nil)
|
|
[_searchMixin controllerLayoutUpdated:[TGViewController screenSizeForInterfaceOrientation:toInterfaceOrientation]];
|
|
}
|
|
|
|
- (void)_layoutTableProgressViews
|
|
{
|
|
_activityIndicator.center = CGPointMake(_nearbyVenuesTableView.frame.size.width / 2, (_nearbyVenuesTableView.frame.size.height - [TGLocationPickerController mapHeight] + 20) / 2 + (_nearbyVenuesTableView.contentInset.top + _nearbyVenuesTableView.contentOffset.y) / 2);
|
|
|
|
_messageLabel.frame = CGRectMake(0, _activityIndicator.center.y - _messageLabel.frame.size.height, _messageLabel.frame.size.width, _messageLabel.frame.size.height);
|
|
}
|
|
|
|
+ (CGFloat)mapHeight
|
|
{
|
|
static dispatch_once_t onceToken;
|
|
static CGFloat mapHeight = 0;
|
|
dispatch_once(&onceToken, ^
|
|
{
|
|
mapHeight = [TGViewController isWidescreen] ? TGLocationPickerMapWidescreenHeight : TGLocationPickerMapHeight;
|
|
});
|
|
return mapHeight;
|
|
}
|
|
|
|
@end
|