talk-ios/NextcloudTalk/ShareLocationViewController.m

415 строки
19 KiB
Objective-C

/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#import "ShareLocationViewController.h"
#import <CoreLocation/CoreLocation.h>
#import "GeoLocationRichObject.h"
#import "NCAPIController.h"
#import "NCAppBranding.h"
#import "NextcloudTalk-Swift.h"
typedef enum ShareLocationSection {
kShareLocationSectionCurrent = 0,
kShareLocationSectionDropPin,
kShareLocationSectionNearby,
kShareLocationSectionNumber
} ShareLocationSection;
@interface ShareLocationViewController () <CLLocationManagerDelegate, MKMapViewDelegate, UITableViewDelegate, UITableViewDataSource, UISearchControllerDelegate, UISearchResultsUpdating>
{
UISearchController *_searchController;
UITableViewController *_resultTableViewController;
CLLocationManager *_locationManager;
CLLocation *_currentLocation;
NSArray *_nearbyPlaces;
NSArray *_searchedPlaces;
BOOL _hasBeenCentered;
MKPointAnnotation *_dropPinAnnotation;
CLPlacemark *_dropPinPlacemark;
UIView *_dropPinGuideView;
UIImageSymbolConfiguration *_iconsConfiguration;
}
@end
@implementation ShareLocationViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = NSLocalizedString(@"Share location", nil);
[self.navigationController.navigationBar setTitleTextAttributes:
@{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]}];
self.navigationController.navigationBar.tintColor = [NCAppBranding themeTextColor];
self.navigationController.navigationBar.barTintColor = [NCAppBranding themeColor];
self.navigationController.navigationBar.translucent = NO;
_iconsConfiguration = [UIImageSymbolConfiguration configurationWithPointSize:20];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[_locationManager requestWhenInUseAuthorization];
self.mapView.delegate = self;
self.mapView.showsUserLocation = YES;
self.myLocationButton.layer.cornerRadius = 22;
self.myLocationButton.clipsToBounds = YES;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self action:@selector(cancelButtonPressed)];
self.navigationItem.leftBarButtonItem = cancelButton;
_resultTableViewController = [[UITableViewController alloc] init];
_resultTableViewController.tableView.delegate = self;
_resultTableViewController.tableView.dataSource = self;
_resultTableViewController.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
_searchController = [[UISearchController alloc] initWithSearchResultsController:_resultTableViewController];
_searchController.delegate = self;
_searchController.searchResultsUpdater = self;
_searchController.hidesNavigationBarDuringPresentation = NO;
[_searchController.searchBar sizeToFit];
UIColor *themeColor = [NCAppBranding themeColor];
UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
[appearance configureWithOpaqueBackground];
appearance.backgroundColor = themeColor;
appearance.titleTextAttributes = @{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]};
self.navigationItem.standardAppearance = appearance;
self.navigationItem.compactAppearance = appearance;
self.navigationItem.scrollEdgeAppearance = appearance;
self.navigationItem.searchController = _searchController;
self.navigationItem.searchController.searchBar.searchTextField.backgroundColor = [NCUtils searchbarBGColorForColor:themeColor];
if (@available(iOS 16.0, *)) {
self.navigationItem.preferredSearchBarPlacement = UINavigationItemSearchBarPlacementStacked;
}
_searchController.searchBar.tintColor = [NCAppBranding themeTextColor];
UITextField *searchTextField = [_searchController.searchBar valueForKey:@"searchField"];
UIButton *clearButton = [searchTextField valueForKey:@"_clearButton"];
searchTextField.tintColor = [NCAppBranding themeTextColor];
searchTextField.textColor = [NCAppBranding themeTextColor];
dispatch_async(dispatch_get_main_queue(), ^{
// Search bar placeholder
searchTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"Search for places", nil)
attributes:@{NSForegroundColorAttributeName:[[NCAppBranding themeTextColor] colorWithAlphaComponent:0.5]}];
// Search bar search icon
UIImageView *searchImageView = (UIImageView *)searchTextField.leftView;
searchImageView.image = [searchImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[searchImageView setTintColor:[[NCAppBranding themeTextColor] colorWithAlphaComponent:0.5]];
// Search bar search clear button
UIImage *clearButtonImage = [clearButton.imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[clearButton setImage:clearButtonImage forState:UIControlStateNormal];
[clearButton setImage:clearButtonImage forState:UIControlStateHighlighted];
[clearButton setTintColor:[NCAppBranding themeTextColor]];
});
// Place resultTableViewController correctly
self.definesPresentationContext = YES;
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager
{
CLAuthorizationStatus status = manager.authorizationStatus;
_myLocationButton.hidden = YES;
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways) {
_myLocationButton.hidden = NO;
} else if (status == kCLAuthorizationStatusNotDetermined) {
[_locationManager requestWhenInUseAuthorization];
} else if (status == kCLAuthorizationStatusDenied) {
[self showAuthorizationStatusDeniedAlert];
}
}
- (void)showAuthorizationStatusDeniedAlert
{
UIAlertController * alert = [UIAlertController
alertControllerWithTitle:NSLocalizedString(@"Could not access your location", nil)
message:NSLocalizedString(@"Location service has been denied. Check your settings.", nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* settingsButton = [UIAlertAction actionWithTitle:NSLocalizedString(@"Settings", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
}];
[alert addAction:settingsButton];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - MKMapViewDelegate
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
// Center the map view the first time the user location is updated
if (!_hasBeenCentered) {
_hasBeenCentered = YES;
[self centerMapViewToUserLocation];
}
dispatch_async(dispatch_get_main_queue(), ^{
self->_currentLocation = mapView.userLocation.location;
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:kShareLocationSectionCurrent] withRowAnimation:UITableViewRowAnimationNone];
});
}
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
[_mapView removeAnnotation:_dropPinAnnotation];
[self showDropPinGuideView];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
[self hideDropPinGuideView];
_dropPinAnnotation = [[MKPointAnnotation alloc] init];
_dropPinAnnotation.coordinate = CLLocationCoordinate2DMake(_mapView.centerCoordinate.latitude, _mapView.centerCoordinate.longitude);
[_mapView addAnnotation:_dropPinAnnotation];
CLLocation *location = [[CLLocation alloc] initWithLatitude:_dropPinAnnotation.coordinate.latitude longitude:_dropPinAnnotation.coordinate.longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
if (!error) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_dropPinPlacemark = placemarks[0];
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:kShareLocationSectionDropPin] withRowAnimation:UITableViewRowAnimationNone];
});
}
}];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
// If the annotation is the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
if (annotation == _dropPinAnnotation) {
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:@"SelectedLocationAnnotationView"];
if (!pinView) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"SelectedLocationAnnotationView"];
pinView.pinTintColor = [NCAppBranding elementColor];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
} else {
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
#pragma mark - Actions
- (void)cancelButtonPressed
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)myLocationButtonPressed:(id)sender
{
[self centerMapViewToUserLocation];
}
#pragma mark - Map View
- (void)centerMapViewToUserLocation
{
MKCoordinateRegion mapRegion;
mapRegion.center = self.mapView.userLocation.coordinate;
mapRegion.span = MKCoordinateSpanMake(0.01, 0.01);
[self.mapView setRegion:mapRegion animated: YES];
}
- (void)showDropPinGuideView
{
_dropPinGuideView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 8, 8)];
_dropPinGuideView.backgroundColor = [NCAppBranding placeholderColor];
_dropPinGuideView.layer.cornerRadius = 4;
_dropPinGuideView.clipsToBounds = YES;
_dropPinGuideView.center = _mapView.center;
[self.mapView addSubview:_dropPinGuideView];
[self.mapView bringSubviewToFront:_dropPinGuideView];
}
- (void)hideDropPinGuideView
{
[_dropPinGuideView removeFromSuperview];
_dropPinAnnotation = nil;
}
#pragma mark - Search places
- (void)searchForNearbyPlaces
{
MKLocalPointsOfInterestRequest *request = [[MKLocalPointsOfInterestRequest alloc] initWithCoordinateRegion:self.mapView.region];
MKLocalSearch *search = [[MKLocalSearch alloc] initWithPointsOfInterestRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse * _Nullable response, NSError * _Nullable error) {
if (response) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_nearbyPlaces = response.mapItems;
[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:kShareLocationSectionNearby] withRowAnimation:UITableViewRowAnimationNone];
});
}
}];
}
- (void)searchForPlacesWithString:(NSString *)searchString
{
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] initWithNaturalLanguageQuery:searchString];
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse * _Nullable response, NSError * _Nullable error) {
if (response) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_searchedPlaces = response.mapItems;
[self->_resultTableViewController.tableView reloadData];
});
}
}];
}
#pragma mark - Search controller
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
[self searchForPlacesWithString:_searchController.searchBar.text];
}
#pragma mark - UITableView delegate and data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == _resultTableViewController.tableView) {
return _searchedPlaces.count;
}
if (section == kShareLocationSectionCurrent) {
return _currentLocation ? 1 : 0;
} else if (section == kShareLocationSectionDropPin) {
return 1;
} else if (section == kShareLocationSectionNearby) {
return _nearbyPlaces.count;
}
return 0;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
if (tableView == _resultTableViewController.tableView) {
return 1;
}
return kShareLocationSectionNumber;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if (section == kShareLocationSectionNearby && _nearbyPlaces.count > 0) {
return NSLocalizedString(@"Nearby places", nil);;
}
return nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Search result table view
if (tableView == _resultTableViewController.tableView) {
MKMapItem *searchedPlace = [_searchedPlaces objectAtIndex:indexPath.row];
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"SearchedLocationCellIdentifier"];
[cell.imageView setImage:[UIImage systemImageNamed:@"mappin" withConfiguration:_iconsConfiguration]];
cell.imageView.tintColor = [UIColor secondaryLabelColor];
cell.textLabel.text = searchedPlace.name;
NSString *subtitle = nil;
if (searchedPlace.placemark.thoroughfare && searchedPlace.placemark.subThoroughfare) {
subtitle = [NSString stringWithFormat:@"%@ %@, ", searchedPlace.placemark.thoroughfare, searchedPlace.placemark.subThoroughfare];
}
if (searchedPlace.placemark.locality) {
subtitle = [subtitle stringByAppendingString:[NSString stringWithFormat:@"%@, ", searchedPlace.placemark.locality]];
}
if (searchedPlace.placemark.country) {
subtitle = [subtitle stringByAppendingString:[NSString stringWithFormat:@"%@", searchedPlace.placemark.country]];;
}
cell.detailTextLabel.text = subtitle;
return cell;
}
// Main view table view
if (indexPath.section == kShareLocationSectionCurrent) {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"UserLocationCellIdentifier"];
[cell.imageView setImage:[UIImage systemImageNamed:@"location.fill" withConfiguration:_iconsConfiguration]];
cell.textLabel.text = NSLocalizedString(@"Share current location", nil);
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@: %.0fm", NSLocalizedString(@"Accuracy", nil), _currentLocation.horizontalAccuracy];
return cell;
} else if (indexPath.section == kShareLocationSectionDropPin) {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"DropPinCellIdentifier"];
[cell.imageView setImage:[UIImage systemImageNamed:@"mappin" withConfiguration:_iconsConfiguration]];
cell.textLabel.text = NSLocalizedString(@"Share pin location", @"Share the location of a pin that has been dropped in a map view");
if (_dropPinPlacemark.thoroughfare && _dropPinPlacemark.subThoroughfare) {
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ %@", _dropPinPlacemark.thoroughfare, _dropPinPlacemark.subThoroughfare];
}
return cell;
} else if (indexPath.section == kShareLocationSectionNearby) {
MKMapItem *nearbyPlace = [_nearbyPlaces objectAtIndex:indexPath.row];
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"NearbyLocationCellIdentifier"];
[cell.imageView setImage:[UIImage systemImageNamed:@"mappin" withConfiguration:_iconsConfiguration]];
cell.imageView.tintColor = [UIColor secondaryLabelColor];
cell.textLabel.text = nearbyPlace.name;
cell.detailTextLabel.text = nil;
if (nearbyPlace.placemark.thoroughfare && nearbyPlace.placemark.subThoroughfare) {
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ %@", nearbyPlace.placemark.thoroughfare, nearbyPlace.placemark.subThoroughfare];
}
return cell;
}
return nil;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Search result table view
if (tableView == _resultTableViewController.tableView) {
MKMapItem *searchedPlace = [_searchedPlaces objectAtIndex:indexPath.row];
[self.delegate shareLocationViewController:self didSelectLocationWithLatitude:searchedPlace.placemark.location.coordinate.latitude longitude:searchedPlace.placemark.location.coordinate.longitude andName:searchedPlace.name];
[self dismissViewControllerAnimated:YES completion:nil];
} else {
// Main view table view
if (indexPath.section == kShareLocationSectionCurrent) {
[self.delegate shareLocationViewController:self didSelectLocationWithLatitude:_currentLocation.coordinate.latitude longitude:_currentLocation.coordinate.longitude andName:NSLocalizedString(@"My location", nil)];
} else if (indexPath.section == kShareLocationSectionDropPin) {
NSString *locationName = NSLocalizedString(@"Shared location", nil);
if (_dropPinPlacemark.thoroughfare && _dropPinPlacemark.subThoroughfare) {
locationName = [NSString stringWithFormat:@"%@ %@", _dropPinPlacemark.thoroughfare, _dropPinPlacemark.subThoroughfare];
}
[self.delegate shareLocationViewController:self didSelectLocationWithLatitude:_dropPinAnnotation.coordinate.latitude longitude:_dropPinAnnotation.coordinate.longitude andName:locationName];
} else if (indexPath.section == kShareLocationSectionNearby) {
MKMapItem *nearbyPlace = [_nearbyPlaces objectAtIndex:indexPath.row];
[self.delegate shareLocationViewController:self didSelectLocationWithLatitude:nearbyPlace.placemark.location.coordinate.latitude longitude:nearbyPlace.placemark.location.coordinate.longitude andName:nearbyPlace.name];
}
}
}
@end