/* * Copyright 2016 Google Inc. All rights reserved. * * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF * ANY KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ #import "GooglePlacesDemos/DemoListViewController.h" #import // The cell reuse identifier we are going to use. static NSString *const kCellIdentifier = @"DemoCellIdentifier"; static const CGFloat kSelectionHeight = 40; static const CGFloat kSelectionSwitchWidth = 50; static const CGFloat kEdgeBuffer = 8; @implementation DemoListViewController { UIViewController *_editSelectionsViewController; NSMutableDictionary *_autocompleteFiltersSelectionMap; NSMutableDictionary *_placeFieldsSelectionMap; NSMutableDictionary *_restrictionBoundsMap; CGFloat _nextSelectionYPos; DemoData *_demoData; } - (instancetype)initWithDemoData:(DemoData *)demoData { if ((self = [self init])) { _demoData = demoData; _autocompleteFiltersSelectionMap = [NSMutableDictionary dictionary]; _placeFieldsSelectionMap = [NSMutableDictionary dictionary]; _restrictionBoundsMap = [NSMutableDictionary dictionary]; } return self; } - (void)viewWillAppear:(BOOL)animated { // Set up the title for view to be displayed. self.title = [DemoListViewController titleText]; [super viewWillAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { // Clear the title to make room for next view to share the header space in splitsreen view. self.title = nil; [super viewWillDisappear:animated]; } - (void)viewDidLoad { [super viewDidLoad]; // Set up the edit selections UI. [self setUpEditSelectionsUI]; // Add button to the header to edit the place field selections. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(beginEditSelections)]; // Register a plain old UITableViewCell as this will be sufficient for our list. [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellIdentifier]; // [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]]; } /** * Private method which is called when a demo is selected. Constructs the demo view controller and * displays it. * * @param demo The demo to show. */ - (void)showDemo:(Demo *)demo { CLLocationCoordinate2D northEast = kCLLocationCoordinate2DInvalid; CLLocationCoordinate2D southWest = kCLLocationCoordinate2DInvalid; GMSAutocompleteFilter *autocompleteFilter = [self autcompleteFilter]; // Check for restriction bounds settings. if (_restrictionBoundsMap[@"Kansas"].on) { northEast = CLLocationCoordinate2DMake(39.0, -95.0); southWest = CLLocationCoordinate2DMake(37.5, -100.0); autocompleteFilter.origin = [[CLLocation alloc] initWithLatitude:northEast.latitude longitude:northEast.longitude]; autocompleteFilter.locationRestriction = GMSPlaceRectangularLocationOption(northEast, southWest); } else if (_restrictionBoundsMap[@"Canada"].on) { northEast = CLLocationCoordinate2DMake(70.0, -60.0); southWest = CLLocationCoordinate2DMake(50.0, -140.0); autocompleteFilter.origin = [[CLLocation alloc] initWithLatitude:northEast.latitude longitude:northEast.longitude]; autocompleteFilter.locationRestriction = GMSPlaceRectangularLocationOption(northEast, southWest); } // Create view controller with the autocomplete filters, bounds and selected place fields. UIViewController *viewController = [demo createViewControllerWithAutocompleteFilter:autocompleteFilter placeFields:[self selectedPlaceFields]]; [self.navigationController pushViewController:viewController animated:YES]; } #pragma mark - Edit Autocomplete Filters and Place Fields selections UI - (void)setUpEditSelectionsUI { // Initialize the place fields selection UI. UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; scrollView.backgroundColor = [UIColor whiteColor]; // Add heading for the autocomplete type filters. _nextSelectionYPos = [UIApplication sharedApplication].statusBarFrame.size.height; [scrollView addSubview:[self headerLabelForTitle:@"Autcomplete Filters"]]; // Set up the individual autocomplete type filters we can limit the results to. // Add a heading for the place fields that we can request. _nextSelectionYPos += kSelectionHeight; for (NSInteger autocompleteFilterType = kGMSPlacesAutocompleteTypeFilterGeocode; autocompleteFilterType <= kGMSPlacesAutocompleteTypeFilterCity; ++autocompleteFilterType) { [scrollView addSubview:[self selectionButtonForAutocompleteFilterType:(GMSPlacesAutocompleteTypeFilter) autocompleteFilterType]]; } // Add heading for the autocomplete restriction bounds. [scrollView addSubview:[self headerLabelForTitle:@"Autcomplete Restriction Bounds"]]; // Set up the restriction bounds for testing purposes. _nextSelectionYPos += kSelectionHeight; [scrollView addSubview:[self selectionButtonForRestrictionBoundsArea:@"Canada"]]; _nextSelectionYPos += kSelectionHeight; [scrollView addSubview:[self selectionButtonForRestrictionBoundsArea:@"Kansas"]]; // Add heading for the place fields that we can request. _nextSelectionYPos += kSelectionHeight; [scrollView addSubview:[self headerLabelForTitle:@"Place Fields"]]; // Set up the individual place fields that we can request. _nextSelectionYPos += kSelectionHeight; for (NSUInteger placeField = GMSPlaceFieldName; placeField <= GMSPlaceFieldUTCOffsetMinutes; placeField <<= 1) { [scrollView addSubview:[self selectionButtonForPlaceField:(GMSPlaceField)placeField]]; } // Add the close button to dismiss the selection UI. UIButton *close = [[UIButton alloc] initWithFrame:CGRectMake(0, _nextSelectionYPos, self.view.frame.size.width, kSelectionHeight)]; close.backgroundColor = [UIColor blueColor]; [close setTitle:@"Close" forState:UIControlStateNormal]; [close setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [close addTarget:self action:@selector(endEditSelections:) forControlEvents:UIControlEventTouchUpInside]; [scrollView addSubview:close]; // Set the content size for the scroll view after we determine the height of all the contents. scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, close.frame.origin.y + close.frame.size.height + kEdgeBuffer); // Initialize the edit selections view controller. _editSelectionsViewController = [[UIViewController alloc] init]; _editSelectionsViewController.view = scrollView; } - (void)updateFrameForSelectionViews:(UIView *)view { CGFloat horizontalInset = [self horizontalInset]; view.frame = CGRectMake(horizontalInset, view.frame.origin.y, self.view.frame.size.width - (2 * horizontalInset), view.frame.size.height); for (UIView *subView in view.subviews) { if ([subView isKindOfClass:[UISwitch class]]) { subView.frame = CGRectMake(view.frame.size.width - kSelectionSwitchWidth, subView.frame.origin.y, subView.frame.size.width, subView.frame.size.height); } } } - (UILabel *)headerLabelForTitle:(NSString *)title { UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, _nextSelectionYPos, self.view.frame.size.width, kSelectionHeight)]; headerLabel.backgroundColor = [UIColor lightGrayColor]; headerLabel.text = title; headerLabel.textAlignment = NSTextAlignmentCenter; headerLabel.textColor = [UIColor whiteColor]; headerLabel.userInteractionEnabled = NO; return headerLabel; } - (UIButton *)selectionButtonForTitle:(NSString *)title { UIButton *selectionButton = [[UIButton alloc] initWithFrame:CGRectMake(0, _nextSelectionYPos, self.view.frame.size.width, kSelectionHeight)]; UISwitch *selectionSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(selectionButton.frame.size.width - kSelectionSwitchWidth, kEdgeBuffer / 2, kSelectionSwitchWidth, kSelectionHeight)]; selectionSwitch.userInteractionEnabled = NO; [selectionButton addTarget:self action:@selector(selectionButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; [selectionButton setBackgroundColor:[UIColor whiteColor]]; [selectionButton setTitle:title forState:UIControlStateNormal]; [selectionButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; selectionButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; [selectionButton addSubview:selectionSwitch]; return selectionButton; } - (UIButton *)selectionButtonForPlaceField:(GMSPlaceField)placeField { NSDictionary *fieldsMapping = @{ @(GMSPlaceFieldName) : @"Name", @(GMSPlaceFieldPlaceID) : @"Place ID", @(GMSPlaceFieldPlusCode) : @"Plus Code", @(GMSPlaceFieldCoordinate) : @"Coordinate", @(GMSPlaceFieldOpeningHours) : @"Opening Hours", @(GMSPlaceFieldPhoneNumber) : @"Phone Number", @(GMSPlaceFieldFormattedAddress) : @"Formatted Address", @(GMSPlaceFieldRating) : @"Rating", @(GMSPlaceFieldUserRatingsTotal) : @"User Ratings Total", @(GMSPlaceFieldPriceLevel) : @"Price Level", @(GMSPlaceFieldTypes) : @"Types", @(GMSPlaceFieldWebsite) : @"Website", @(GMSPlaceFieldViewport) : @"Viewport", @(GMSPlaceFieldAddressComponents) : @"Address Components", @(GMSPlaceFieldPhotos) : @"Photos", @(GMSPlaceFieldUTCOffsetMinutes) : @"UTC Offset Minutes", }; UIButton *selectionButton = [self selectionButtonForTitle:fieldsMapping[@(placeField)]]; UISwitch *selectionSwitch = [self switchFromButton:selectionButton]; [selectionSwitch setOn:YES]; _placeFieldsSelectionMap[@(placeField)] = selectionSwitch; _nextSelectionYPos += selectionButton.frame.size.height; return selectionButton; } - (UIButton *)selectionButtonForAutocompleteFilterType: (GMSPlacesAutocompleteTypeFilter)autocompleteFilter { NSDictionary *fieldsMapping = @{ @(kGMSPlacesAutocompleteTypeFilterGeocode) : @"Geocode", @(kGMSPlacesAutocompleteTypeFilterAddress) : @"Address", @(kGMSPlacesAutocompleteTypeFilterEstablishment) : @"Establishment", @(kGMSPlacesAutocompleteTypeFilterRegion) : @"Region", @(kGMSPlacesAutocompleteTypeFilterCity) : @"City", }; UIButton *selectionButton = [self selectionButtonForTitle:fieldsMapping[@(autocompleteFilter)]]; [selectionButton addTarget:self action:@selector(disableOtherAutocompleteFilterExceptForTapped:) forControlEvents:UIControlEventTouchUpInside]; UISwitch *selectionSwitch = [self switchFromButton:selectionButton]; [selectionSwitch setOn:NO]; _autocompleteFiltersSelectionMap[@(autocompleteFilter)] = selectionSwitch; _nextSelectionYPos += selectionButton.frame.size.height; return selectionButton; } - (UIButton *)selectionButtonForRestrictionBoundsArea:(NSString *)area { UIButton *selectionButton = [self selectionButtonForTitle:area]; [selectionButton addTarget:self action:@selector(disableOtherRestrictionBoundsExceptForTapped:) forControlEvents:UIControlEventTouchUpInside]; _restrictionBoundsMap[area] = [self switchFromButton:selectionButton]; [_restrictionBoundsMap[area] setOn:NO]; return selectionButton; } - (UISwitch *)switchFromButton:(UIButton *)button { for (UIView *subView in button.subviews) { if ([subView isKindOfClass:[UISwitch class]]) { return (UISwitch *)subView; } } return nil; } - (void)selectionButtonTapped:(UIButton *)sender { UISwitch *selectionSwitch = [self switchFromButton:sender]; [selectionSwitch setOn:selectionSwitch.on ? NO : YES animated:YES]; } - (void)disableOtherAutocompleteFilterExceptForTapped:(UIButton *)sender { UISwitch *tappedSwitch = [self switchFromButton:sender]; for (NSNumber *number in _autocompleteFiltersSelectionMap) { UISwitch *selectionSwitch = _autocompleteFiltersSelectionMap[number]; if (selectionSwitch != tappedSwitch) { [selectionSwitch setOn:NO animated:YES]; } } } - (void)disableOtherRestrictionBoundsExceptForTapped:(UIButton *)sender { UISwitch *tappedSwitch = [self switchFromButton:sender]; for (NSString *key in [_restrictionBoundsMap allKeys]) { UISwitch *selectionSwitch = _restrictionBoundsMap[key]; if (selectionSwitch != tappedSwitch) { [selectionSwitch setOn:NO animated:YES]; } } } - (void)beginEditSelections { // Update the selection views to fit the current device frame and orientation. for (UIView *view in _editSelectionsViewController.view.subviews) { [self updateFrameForSelectionViews:view]; } // Scroll the contents to the top before presenting the selection UI. UIScrollView *scrollView = (UIScrollView *)_editSelectionsViewController.view; [scrollView setContentOffset:CGPointZero animated:NO]; // Present the selection UI to edit which place fields to request. [self.navigationController presentViewController:_editSelectionsViewController animated:YES completion:nil]; } - (void)endEditSelections:(UIButton *)sender { [_editSelectionsViewController dismissViewControllerAnimated:YES completion:nil]; } - (GMSAutocompleteFilter *)autcompleteFilter { GMSAutocompleteFilter *filter = [[GMSAutocompleteFilter alloc] init]; for (NSNumber *number in _autocompleteFiltersSelectionMap) { UISwitch *selectionSwitch = _autocompleteFiltersSelectionMap[number]; if ([selectionSwitch isOn]) { filter.type = (GMSPlacesAutocompleteTypeFilter)[number integerValue]; break; } } return filter; } - (GMSPlaceField)selectedPlaceFields { GMSPlaceField placeFields = 0; for (NSNumber *number in _placeFieldsSelectionMap) { UISwitch *selectionSwitch = _placeFieldsSelectionMap[number]; if ([selectionSwitch isOn]) { placeFields |= [number integerValue]; } } return placeFields; } - (CGFloat)horizontalInset { #if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) // Take into account the safe areas of the device screen and do not use that space. if (@available(iOS 11.0, *)) { return MAX(self.view.safeAreaInsets.left, self.view.safeAreaInsets.right) + kEdgeBuffer; } #endif return kEdgeBuffer; } #pragma mark - UITableViewDataSource/Delegate - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return _demoData.sections.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _demoData.sections[section].demos.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Dequeue a table view cell to use. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath]; // Grab the demo object. Demo *demo = _demoData.sections[indexPath.section].demos[indexPath.row]; // Configure the demo title on the cell. cell.textLabel.text = demo.title; return cell; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return _demoData.sections[section].title; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Get the demo which was selected. Demo *demo = _demoData.sections[indexPath.section].demos[indexPath.row]; [self showDemo:demo]; } + (NSString *)titleText { NSString *titleFormat = NSLocalizedString( @"App.NameAndVersion", @"The name of the app to display in a navigation bar along with a " @"placeholder for the SDK version number"); return [NSString stringWithFormat:titleFormat, [GMSPlacesClient SDKLongVersion]]; } #pragma mark - Handle Orientation Changes - (void)orientationChanged:(NSNotification *)notification { // Dismiss the selections UI if currently active. if (_editSelectionsViewController.isViewLoaded && _editSelectionsViewController.view.window) { [self endEditSelections:nil]; } } @end