123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- /**
- * 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(@"Cancel", 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
|