You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

354 lines
13 KiB

// Copyright 2021 Google LLC. 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 GooglePlaces
import UIKit
/// The section of configuration.
struct ConfigSection {
let name: String
let samples: [ConfigData]
}
/// The configuration data.
struct ConfigData {
let name: String
let tag: Int
let action: Selector
}
/// The location option for autocomplete search.
enum LocationOption: Int {
case unspecified = 100
case canada = 101
case kansas = 102
var northEast: CLLocationCoordinate2D {
switch self {
case .canada:
return CLLocationCoordinate2D(latitude: 70.0, longitude: -60.0)
case .kansas:
return CLLocationCoordinate2D(latitude: 39.0, longitude: -95.0)
default:
return CLLocationCoordinate2D()
}
}
var southWest: CLLocationCoordinate2D {
switch self {
case .canada:
return CLLocationCoordinate2D(latitude: 50.0, longitude: -140.0)
case .kansas:
return CLLocationCoordinate2D(latitude: 37.5, longitude: -100.0)
default:
return CLLocationCoordinate2D()
}
}
}
/// Manages the configuration options view for the demo app.
class ConfigurationViewController: UIViewController {
// MARK: - Properties
private let cellIdentifier = "cellIdentifier"
private let filterTagBase = 1000
private lazy var configurationSections: [ConfigSection] = {
var sections: [ConfigSection] = []
let autocompleteFiltersSelector = #selector(autocompleteFiltersSwitch)
let geocode = ConfigData(
name: "Geocode", tag: filterTagBase + GMSPlacesAutocompleteTypeFilter.geocode.rawValue,
action: autocompleteFiltersSelector)
let address = ConfigData(
name: "Address", tag: filterTagBase + GMSPlacesAutocompleteTypeFilter.address.rawValue,
action: autocompleteFiltersSelector)
let establishment = ConfigData(
name: "Establishment",
tag: filterTagBase + GMSPlacesAutocompleteTypeFilter.establishment.rawValue,
action: autocompleteFiltersSelector)
let region = ConfigData(
name: "Region", tag: filterTagBase + GMSPlacesAutocompleteTypeFilter.region.rawValue,
action: autocompleteFiltersSelector)
let city = ConfigData(
name: "City", tag: filterTagBase + GMSPlacesAutocompleteTypeFilter.city.rawValue,
action: autocompleteFiltersSelector)
sections.append(
ConfigSection(
name: "Autocomplete filters", samples: [geocode, address, establishment, region, city]))
let canada = ConfigData(
name: "Canada", tag: LocationOption.canada.rawValue, action: #selector(canadaSwitch))
let kansas = ConfigData(
name: "Kansas", tag: LocationOption.kansas.rawValue, action: #selector(kansasSwitch))
sections.append(
ConfigSection(name: "Autocomplete Restriction Bounds", samples: [canada, kansas]))
let placesFieldsSelector = #selector(placesFieldsSwitch)
let name = ConfigData(
name: "Name", tag: Int(GMSPlaceField.name.rawValue), action: placesFieldsSelector)
let placeId = ConfigData(
name: "Place ID", tag: Int(GMSPlaceField.placeID.rawValue),
action: placesFieldsSelector)
let plusCode = ConfigData(
name: "Plus Code", tag: Int(GMSPlaceField.plusCode.rawValue),
action: placesFieldsSelector)
let coordinate = ConfigData(
name: "Coordinate", tag: Int(GMSPlaceField.coordinate.rawValue),
action: placesFieldsSelector)
let openingHours = ConfigData(
name: "Opening Hours", tag: Int(GMSPlaceField.openingHours.rawValue),
action: placesFieldsSelector)
let phoneNumber = ConfigData(
name: "Phone Number", tag: Int(GMSPlaceField.phoneNumber.rawValue),
action: placesFieldsSelector)
let formattedAddress = ConfigData(
name: "Formatted Address", tag: Int(GMSPlaceField.formattedAddress.rawValue),
action: placesFieldsSelector)
let rating = ConfigData(
name: "Rating", tag: Int(GMSPlaceField.rating.rawValue), action: placesFieldsSelector)
let ratingsTotal = ConfigData(
name: "User Ratings Total", tag: Int(GMSPlaceField.userRatingsTotal.rawValue),
action: placesFieldsSelector)
let priceLevel = ConfigData(
name: "Price Level", tag: Int(GMSPlaceField.priceLevel.rawValue),
action: placesFieldsSelector)
let types = ConfigData(
name: "Types", tag: Int(GMSPlaceField.types.rawValue), action: placesFieldsSelector)
let website = ConfigData(
name: "Website", tag: Int(GMSPlaceField.website.rawValue),
action: placesFieldsSelector)
let viewPort = ConfigData(
name: "Viewport", tag: Int(GMSPlaceField.viewport.rawValue),
action: placesFieldsSelector)
let addressComponents = ConfigData(
name: "Address Components", tag: Int(GMSPlaceField.addressComponents.rawValue),
action: placesFieldsSelector)
let photos = ConfigData(
name: "Photos", tag: Int(GMSPlaceField.photos.rawValue), action: placesFieldsSelector)
let minutes = ConfigData(
name: "UTC Offset Minutes", tag: Int(GMSPlaceField.utcOffsetMinutes.rawValue),
action: placesFieldsSelector)
let status = ConfigData(
name: "Business Status", tag: Int(GMSPlaceField.businessStatus.rawValue),
action: placesFieldsSelector)
let iconImageURL = ConfigData(
name: "Icon Image URL", tag: Int(GMSPlaceField.iconImageURL.rawValue),
action: placesFieldsSelector)
let iconBackgroundColor = ConfigData(
name: "Icon Background Color", tag: Int(GMSPlaceField.iconBackgroundColor.rawValue),
action: placesFieldsSelector)
sections.append(
ConfigSection(
name: "Place Fields",
samples: [
name, placeId, plusCode, coordinate, openingHours, phoneNumber, formattedAddress, rating,
ratingsTotal, priceLevel, types, website, viewPort, addressComponents, photos, minutes,
status, iconImageURL, iconBackgroundColor,
]))
return sections
}()
private var configuration: AutocompleteConfiguration
private lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
tableView.dataSource = self
tableView.delegate = self
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
private lazy var closeButton: UIButton = {
let button = UIButton()
button.backgroundColor = .blue
button.setTitle("Close", for: .normal)
button.setTitleColor(.white, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(tapCloseButton(_:)), for: .touchUpInside)
return button
}()
// MARK: - Public functions
public init(configuration: AutocompleteConfiguration) {
self.configuration = configuration
super.init(nibName: nil, bundle: nil)
}
required init(coder: NSCoder) { fatalError() }
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
view.addSubview(closeButton)
let guide = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: guide.topAnchor),
tableView.bottomAnchor.constraint(equalTo: closeButton.topAnchor),
tableView.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
])
NSLayoutConstraint.activate([
closeButton.bottomAnchor.constraint(equalTo: guide.bottomAnchor),
closeButton.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
closeButton.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
])
}
// MARK: - Private functions
@objc private func tapCloseButton(_ sender: UISwitch) {
dismiss(animated: true)
guard let location = configuration.location else { return }
let northEast = location.northEast
let southWest = location.southWest
// Update configuration
switch location {
case .canada:
configuration.autocompleteFilter.origin = CLLocation(
latitude: northEast.latitude, longitude: northEast.longitude)
configuration.autocompleteFilter.locationRestriction =
GMSPlaceRectangularLocationOption(northEast, southWest)
case .kansas:
configuration.autocompleteFilter.origin = CLLocation(
latitude: northEast.latitude, longitude: northEast.longitude)
configuration.autocompleteFilter.locationRestriction =
GMSPlaceRectangularLocationOption(northEast, southWest)
default:
configuration.autocompleteFilter.origin = nil
configuration.autocompleteFilter.locationRestriction = nil
}
}
@objc private func autocompleteFiltersSwitch(_ sender: UISwitch) {
for sample in configurationSections[0].samples {
guard let switchView = view.viewWithTag(sample.tag) as? UISwitch else { continue }
if switchView.tag != sender.tag {
switchView.setOn(false, animated: true)
}
}
// The value of the type is tag - filterTagBase
guard let type = GMSPlacesAutocompleteTypeFilter(rawValue: sender.tag - filterTagBase) else {
return
}
configuration.autocompleteFilter.type = type
}
@objc private func canadaSwitch(_ sender: UISwitch) {
if sender.isOn {
// Turn off the Kansas switch
guard let switchView = view.viewWithTag(LocationOption.kansas.rawValue) as? UISwitch else {
return
}
switchView.setOn(false, animated: true)
configuration.location = .canada
} else {
configuration.location = .unspecified
}
}
@objc private func kansasSwitch(_ sender: UISwitch) {
if sender.isOn {
// Turn off the Canada switch
guard let switchView = view.viewWithTag(LocationOption.canada.rawValue) as? UISwitch else {
return
}
switchView.setOn(false, animated: true)
configuration.location = .kansas
} else {
configuration.location = .unspecified
}
}
@objc private func placesFieldsSwitch(_ sender: UISwitch) {
var field = UInt(sender.tag)
if sender.isOn {
field |= configuration.placeFields.rawValue
} else {
field = ~field & configuration.placeFields.rawValue
}
configuration.placeFields = GMSPlaceField(rawValue: field)
}
}
extension ConfigurationViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard section <= configurationSections.count else {
return 0
}
return configurationSections[section].samples.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell
{
let cell = tableView.dequeueReusableCell(
withIdentifier: cellIdentifier, for: indexPath)
guard
indexPath.section < configurationSections.count
&& indexPath.row < configurationSections[indexPath.section].samples.count
else { return cell }
let sample = configurationSections[indexPath.section].samples[indexPath.row]
cell.textLabel?.text = sample.name
let switchView = UISwitch(frame: .zero)
switchView.tag = Int(sample.tag)
switch indexPath.section {
case 0:
if sample.tag - filterTagBase == configuration.autocompleteFilter.type.rawValue {
switchView.setOn(true, animated: false)
}
case 1:
let isOn = (sample.tag == configuration.location?.rawValue)
switchView.setOn(isOn, animated: false)
case 2:
if configuration.placeFields == .all {
switchView.setOn(true, animated: false)
} else {
let field = Int(configuration.placeFields.rawValue)
if (field & switchView.tag) != 0 {
switchView.setOn(true, animated: false)
}
}
default:
break
}
switchView.addTarget(self, action: sample.action, for: .valueChanged)
cell.accessoryView = switchView
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
configurationSections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard section <= configurationSections.count else {
return ""
}
return configurationSections[section].name
}
}
extension ConfigurationViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sample = configurationSections[indexPath.section].samples[indexPath.row]
let cell = tableView.cellForRow(at: indexPath)
guard let switchView = cell?.accessoryView as? UISwitch else { return }
switchView.setOn(!switchView.isOn, animated: true)
perform(sample.action, with: switchView)
}
}