Google Maps iOS SDK

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

Вплоть до iOS 5 Google Maps являлись неотъемлемой частью iOS и использовались всеми iOS устройствами. С выходом iOS 6 в 2012 году компания Apple приняла решение и заменила Google Maps внутренним собственным двигателем картографирования.

Всего несколько месяцев спустя Google выпустила свое собственное автономное приложение Google Maps для iOS, а также SDK Google Maps для разработчиков под iOS.

И у MapKit и у Google Maps iOS SDK есть и преимущества и недостатки, но этот туториал покажет вам как внедрить Google Maps в ваше приложение, и вы увидите, насколько хорошо оно может работать в ваших приложениях по геолокации.

В этом туториале вы создадите приложение под названием "Feed Me" (Покорми меня), которое определяет текущее местоположение пользователя и ищет близлежащие места, где можно поесть, попить, или найдет магазины, где можно купить еду. Результаты будут представлены в приложении с помощью SDK Google Maps iOS.

Это руководство предполагает, что вы имеете некоторое представление о Swift и iOS программировании. 

Этот туториал также требует представление о Cocoapods. Для того чтобы следовать этому туториалу, у вас должен быть установлен Cocoapods,

Этот туториал использует Xcode 7.2 и iOS 9.2, и требует знаний по Auto Layout, Size Classes, и, конечно, Swift.

Начало

Скачайте стартовый проект "Feed Me". Он уже использует Cocoapods, так что откройте его с помощью файла Feed Me.xcworkspace .

Ознакомьтесь с проектом. Обратите внимание на:

  • MapViewController.swift: Это основной view controller этого проекта, и в этом туториале вы будете работать только с этим контроллером.
  • GoogleDataProvider.swift: Это класс - оболочка для работы с Google API. Чуточку позже вы познакомитесь с методами этого класса в этом туториале.
  • GooglePlace.swift: Это модель для размещения результатов, возвращаемых из Google.
  • MarkerInfoView.swift: Это подкласс UIView, который отображает детальную информацию объектов. Он идет вместе с соответствующим xib файлом.

Прежде чем начать писать код, вам следует понять, как работает приложение. Запустите ваше приложение. Вы увидите вот такой экран:

Сейчас все, что вы видите- это пустой экран с эмблемой в середине. Нажмите кнопку на правой стороне навигационной панели, чтобы увидеть экран TableViewController подобный этому:

На этот момент это все, что вы можете видеть в приложении. Но пришло время добавить немного магии!

Создаем ключи API

Первое, что вам будет нужно - это некоторые ключи API для Google Maps SDK и Google API, которые вы будете использовать. Если у вас еще нет аккаунта Google, то создайте его (это бесплатно!) Войдите в консоль разработчиков Google (Google Developers Console).

Нажмите на Create Project, назовите проект Feed Me, и нажмите кнопку Create:

Выберите API Manager левой панели меню. Найдите и включите эти API:

  • Google Maps SDK для iOS
  • Google Places API Web Service

Теперь переключитесь на вкладку Enabled APIs, и убедитесь, что ваш экран выглядит вот так:

Выберите Credentials под API Manager на левой панели меню. Кликните на New Credentials -> API key -> iOS key для того, чтобы создать ключ Google Maps SDK:

Введите стартовый идентификатор проекта (tutorial.feedme.com) и нажмите кнопку Create:

Создайте еще один ключ, только на этот раз выберите Server key, чтобы создать ключ Places API. Не указывайте текстовое с IP и нажмите кнопку Create. Теперь у вас должно быть серверный ключ и ключ iOS, вот так:

Теперь вы будете использовать оба ключа, но для начала давайте добавим Google Maps iOS SDK.

Добавляем SDK

Откройте Podfile добавьте следующее прямо над end:

pod 'GoogleMaps'

Далее, откройте Terminal и перейдите в каталог, который содержит ваш проект Feed Me с помощью команды cd:

cd ~/Path/To/Folder/Containing/Feed Me

Для того, чтобы установить Google Maps iOS SDK введите следующую команду в терминале:

pod install

Вы должны увидеть результат, похожий на следующий:

Downloading dependencies
Installing GoogleMaps (1.10.4)
Using SwiftyJSON (2.3.0)
Generating Pods project
Integrating client project

Теперь в вашем проекте у вас есть GoogleMaps. Не правда ли, что Cocoapods делают жизнь намного проще? :]

Прежде чем начать писать какой-то фактический код, необходимо создать bridging header и добавить к нему Google Maps. Это потому, что Google Maps написано на Objective-C, и вы должны сделать его доступным для кода на Swift.

Выберите папку Feed Me в навигаторе и выберите File\New\File…, затем выберите шаблон iOS\Source\Objective-C File. Дайте имя вашему файлу, любое, которое вам нравится, вы удалите этот файл на время, а затем сохранить его. При сохранении файла, Xcode предлагает создать для вас Objective-C bridging header файл, вот так:

Нажмите кнопку Yes, и Xcode создаст bridging header файл и добавит его к вашему проекту. Удалите файлы Objective-C (.h и .m), которые вы только что создали, так как они вам больше не нужны.

Откройте вновь созданный Feed Me-Bridging-Header.h и добавьте следующую строку кода в конце файла:

#import <GoogleMaps/GoogleMaps.h>

Заметка

Примечание: На данном этапе вы должны быть в состоянии начать использовать Google Maps SDK, но есть еще один шаг, который вы должны предпринять из-за ошибки в Cocoapods. В Project Navigator, выберите проект Feed Me в самом верху. Выберите цель (таргет) Feed Me, выберите вкладку Build Settings, и в Other Linker Flags добавьте -ObjC, как показано ниже:

Надеюсь, что этот вопрос будет решен в следующем обновлении Cocoapods, и туториал соответственно тоже обновится.

Google Maps iOS SDK теперь доступно в приложении на Swift. Теперь настало самое время для того, чтобы начать писать код! :]

Откройте AppDelegate.swift и замените его содержимое следующим:

import UIKit
 
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
 
  var window: UIWindow?
  // 1
  let googleMapsApiKey = "YOUR_GOOGLE_IOS_API_KEY"
 
  func application(application: UIApplication, didFinishLaunchingWithOptions 
      launchOptions: [NSObject: AnyObject]?) -> Bool {
    // 2
    GMSServices.provideAPIKey(googleMapsApiKey)
    return true
  }
}

Здесь появились два новых элемента:

  1. Константа, хранящая ваш ключ Google API iOS. Замените YOUR_GOOGLE_IOS_API_KEY ключом API Google iOS, который вы создали ранее.
  2. Ваше приложение будет подтверждать сервисы Google Maps с API Key, используя метод класса GMSServices provideAPIKey().

Затем откройте Main.storyboard для того чтобы увидеть Interface Builder. Вызовите библиотеку объектов (Object Library), выбрав третью вкладку в панели инструментов - Утилиты - и затем выберите третью вкладку в панели инструментов библиотеки - библиотека объектов (Object Library) - как показано на скриншоте ниже:

Найдите MapViewController и перетащите на него простой UIView из библиотеки объектов приблизительно в центр view MapViewController. Измените цвет фона на светло-серый. Затем откройте Document Outline (иерархию документа) и с его помощью измените иерархию view, так чтобы древо объектов выглядело следующим образом:

Чтобы изменить этот простой UIView в GMSMapView, выберите view, который вы только что добавили, и откройте Identity Inspector, выбрав третью вкладку слева на панели Utilities toolbar. Измените класс для этого view как показано на GMSMapView, как показано на скриншоте ниже:

Теперь экран вашего MapViewController должен выглядеть примерно вот так:

Далее, вам нужно добавить некоторые констрейнты, чтобы ваша карта заполнила весь экран. Выберите Map View в Иерархии документа , а затем выберите вторую кнопку слева в нижней правой части окна Interface Builder - кнопка Pin (Отметка на карте). Убедитесь, что Constrain to margins не имеет ограничений- это гарантирует, что карта заполнит все доступное пространство на экране - и добавьте 0 ограничение пространства сверху, слева, снизу и справа вашего superview.

Ваш Pin editor должен выглядеть следующим образом:

Кликните на Add 4 Constraints и добавьте новые констрейнты вашему map view. Для того, чтобы обновить фрейм, нажмите кнопку справа от кнопки Pin — кнопку Resolve Auto Layout Issues — и выберите Update Frames.

Ваш экран MapViewController должен выглядеть следующим образом, серая область это GMSMapView:

Перед тем как запустить проект, добавьте IBOutlet в map view. Чтобы сделать это, вызовите Assistant Editor, выбрав вторую вкладку в панели инструментов:

Выберите map view в Interface Builder, удерживая клавишу CTRL + drag линию от map view к MapViewController.swift. Появится всплывающее окно. Установите тип соединения на Outlet и имя на mapView. Тип оставьте как GMSMapView, и щелкните на Connect:

Это создаст свойство GMSMapView в MapViewController.swift and автоматически подключит его к Interface Builder. Так же не забудьте прописать выражения импорта для фреймворка GoogleMaps в самом файле MapViewController.swift на самом верху. Запустите проект, ваша карта должна выглядеть теперь следующим образом:

Теперь вы используете Google Maps iOS SDK в вашем приложении, но вы ведь можете не только просто показывать обычную карту, не так ли?

Определение местоположения

Feed Me определяет места рядом с пользователем, и вы не можете сделать этого, не опередив местоположение самого пользователя.

Во-первых, откройте MapViewController.swift и добавьте следующее свойство:

let locationManager = CLLocationManager()

Это добавит и подтвердит свойство CLLocationManager, названное locationManager.

Далее, найдите viewDidLoad() и добавьте эти две строчки в конце:

locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()

Это сделает MapViewController делегатом locationManager и запросит доступ к местоположению пользователя.

Затем откройте проект Feed Me в верхней части навигатора, выберите Targets -> Feed Me и перейдите на вкладку Info, выберите первую строку в разделе Custom iOS Target Properties и нажмите на значок +, для того чтобы добавить новую строку.

Введите NSLocationWhenInUseUsageDescription в качестве ключа, выберите String как тип, и введите следующий текст в качестве значения:

Предоставляя свое местоположение, вы даете возможность приложению найти вам хорошее место, где можно поесть.

Когда вы закончите, оно должно выглядеть примерно так:

Перед тем, как запустить ваше приложение, вам нужно сделать так, чтобы MapViewController соответствовал протоколу CLLocationManagerDelegate.

Добавьте следующее расширение в нижней части MapViewController.Swift:

// MARK: - CLLocationManagerDelegate
//1
extension MapViewController: CLLocationManagerDelegate {
  // 2
  func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    // 3
    if status == .AuthorizedWhenInUse {
 
      // 4
      locationManager.startUpdatingLocation()
 
      //5
      mapView.myLocationEnabled = true
      mapView.settings.myLocationButton = true
    }
  }
 
  // 6
  func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    if let location = locations.first {
 
      // 7
      mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
 
      // 8
      locationManager.stopUpdatingLocation()
    }
 
  }
}

Рассмотрим пошагово:

  1. Вы создаете расширение MapViewController, которое соответствует CLLocationManagerDelegate.
  2. locationManager(_:didChangeAuthorizationStatus:) вызывается, когда пользователь предоставляет или не предоставляет вам право определения его местоположение.
  3. Здесь вы подтверждаете, что пользователь предоставил вам разрешение в то время как используется приложение.
  4. После того, как будут созданы права, попросите location manager обновить местоположение пользователя.
  5. GMSMapView имеет две особенности, касающиеся местоположения пользователя: myLocationEnabled рисует голубую точку в том месте, где находится пользователь, в то время как myLocationButton, при значении true, добавляет кнопку на карту, которая при прикосновении к экрану, центрирует карту на месте пользователя.
  6. locationManager(_:didUpdateLocations:) выполняется, когда location manager получает новые данные о местоположении.
  7. Это обновляет камеру на карте, которая центрируется вокруг текущего местоположения пользователя. Класс GMSCameraPosition собирает все параметры положения камеры и передает их на карту для отображения.
  8. Сообщаем locationManager ,что вы больше не заинтересованы в апдейтах; Вы не хотите следовать дальше, так как начальное местоположение пользователя вам достаточно для работы.

Запустите ваше приложение. Как только оно загрузится, вам придет сообщение с просьбой разрешить определить ваше местоположение. Нажмите на Allow:

Теперь вы должны увидеть, как карта центрируется вокруг вашего местоположения. Прокрутите карту скроллом и нажмите на кнопку Locate, и карта вернется на место в вашей позицией в центре, вот так:

Реализация геокодирования

Теперь у вас есть местоположение пользователя, было бы неплохо, если бы вы могли предоставить адрес этого местоположения. В Google Maps имеется объект, который именно это и делает: GMSGeocoder. Она берет простую координату и возвращает читаемый почтовый адрес.

Во-первых, вам нужно добавить что-то из интерфейса, чтобы представить адрес пользователю.

Откройте Main.storyboard и добавьте UILabel к MapViewController. Убедитесь, что вы добавили ярлык (лейбл) к view MapViewController, а не к GMSMapView.

Затем откройте Attributes Inspector и добавьте ярлыку (лейблу) следующие атрибуты:

  • Установите Выравнивание по центру (Alignment на center).
  • Установите Lines на 0. Удивительно, но это позволяет ярлыку занимать столько строк, сколько потребуется для того, чтобы вмещался текст. 
  • Установите цвет фона на белый с 85% непрозрачностью (Background на white с 85% opacity).

Attributes Inspector ярлыка и Object Tree должны выглядеть следующим образом, когда вы все это сделаете:

Наконец, добавьте лейблу 0 ограничения слева, снизу и справа, как показано ниже:

Это привязывает лейбл к нижней части экрана и растягивает его по всей ширине. Обновите фреймы и продолжайте.

Ваш экран storyboard должен выглядеть следующим образом:

Затем вам следует создать аутлет (outlet) для лейбла. Выберите Assistant Editor, и CTRL + drag от лейбла в Document Outline к MapViewController.swift. Установите тип подключения на Outlet, имя на addressLabel и нажмите Connect.

Это добавит свойство вашему MapViewController, которое вы сможете использовать в вашем коде:

@IBOutlet weak var addressLabel: UILabel!

Добавьте метод под MapViewController.swift:

func reverseGeocodeCoordinate(coordinate: CLLocationCoordinate2D) {
 
  // 1
  let geocoder = GMSGeocoder()
 
  // 2
  geocoder.reverseGeocodeCoordinate(coordinate) { response, error in
    if let address = response?.firstResult() {
 
      // 3
      let lines = address.lines as! [String]
      self.addressLabel.text = lines.joinWithSeparator("\n")
 
      // 4
      UIView.animateWithDuration(0.25) {
        self.view.layoutIfNeeded()
      }
    }
  }
}

Еще раз разберем пошагово:

  1. Создаем объект GMSGeocoder, который переводит широту и долготу координат в адрес.
  2. Просим геокодер изменить геокодирование координат, переданных методу. Затем проверяем, есть ли адрес в ответ типа GMSAddress. Это модель класса для адресов, возвращенных GMSGeocoder.
  3. Устанавите текст на addressLabel на адреса, возвращенные геокодером.
  4. После того, как адрес установлен, анимируйте изменения в intrinsic content size лейбла.

Вам нужно, чтобы этот метод вызывался каждый раз, когда пользователь меняет свое положение на карте. Для этого вы будете использовать GMSMapViewDelegate.

Добавьте еще одно расширение в нижнюю часть MapViewController.swift, вот так:

// MARK: - GMSMapViewDelegate
extension MapViewController: GMSMapViewDelegate {
 
}

Это объявит, что MapViewController соответствует GMSMapViewDelegate протоколу.

Затем, добавьте следующую строку кода в viewDidLoad():

mapView.delegate = self

Это делает MapViewController делегатом map view.

Наконец, добавьте следующий метод только что добавленному расширению:

func mapView(mapView: GMSMapView!, idleAtCameraPosition position: GMSCameraPosition!) {
  reverseGeocodeCoordinate(position.target)
}

Этот метод вызывается каждый раз, когда карта перестает двигаться и останавливается в новом положении, где затем происходит вызов изменения геокодирования на новое положение и обновление текста в addressLabel.

Запустите ваше приложение. Вы увидите адрес вашего текущего местоположения - реального или смоделированного, который появляется в нижней части экрана:

Заметили, что не так?

Ответ

Лого Google и кнопка Location скрыты лейблом.

К счастью, GMSMapView обеспечивает очень простое решение для этого: padding. Когда используется паддинг, то визуальные элементы будут размещены в соответствии с этим паддингом.

Вернитесь к reverseGeocodeCoordinate(_:), и внесите эти изменения в блок анимации:

// 1
let labelHeight = self.addressLabel.intrinsicContentSize().height
self.mapView.padding = UIEdgeInsets(top: self.topLayoutGuide.length, left: 0, 
    bottom: labelHeight, right: 0)
 
UIView.animateWithDuration(0.25) {
  //2
  self.pinImageVerticalConstraint.constant = ((labelHeight - self.topLayoutGuide.length) * 0.5)
  self.view.layoutIfNeeded()
}

Здесь мы делаем две вещи:

  1. До блока анимации мы добавляем padding (внутренний отступ) в верхней и нижней части карты. Верхний паддинг равен высоте панели навигации, в то время как нижний паддинг равен высоте лейбла.
  2. Обновление позиции Отметки на карте для ее соответствия паддингу карты через настройку его вертикального констрейнта.

Запустите снова приложение; на этот раз логотип Google и кнопка поиска будут двигаться к своим новым позициям, как только лейбл становится видимым:

Подвигайте карту. Вы заметите, что адрес меняется каждый раз, когда происходит остановка на новой позиции. Давайте добавим визуальный эффект, чтобы ослабить смену адреса.

Добавьте следующий метод для расширения GMSMapViewDelegate:

func mapView(mapView: GMSMapView!, willMove gesture: Bool) {
  addressLabel.lock()
}

Этот метод вызывается каждый раз, когда начинает двигаться карта. Он получает Bool, который говорит вам, происходит ли движение от жеста пользователя, например от прокрутки карты, или движение происходит от кода. Вы вызываете lock() в addressLabel, чтобы дать загрузку анимации.

Если есть lock(), то должен быть и unlock(). Добавьте следующее в верхнюю часть замыкания reverseGeocodeCoordinate(_:):

func reverseGeocodeCoordinate(coordinate: CLLocationCoordinate2D) {
  let geocoder = GMSGeocoder()
  geocoder.reverseGeocodeCoordinate(coordinate) { response, error in
 
    //Add this line
    self.addressLabel.unlock()
 
    //Rest of response handling
  }
}

Запустите ваше приложение. Когда вы прокручиваете карту, вы увидите анимацию в лейбле с адресом, вот так:

Ищем, что поесть

Теперь, когда карта установлена, и у вас есть местоположение пользователя, то пришло время, покормить нашего пользователя!

Вы будете использовать Google Places API для поиска места, где можно поесть и попить вокруг местоположения пользователя. Google Places API является свободным API веб-сервисом, который можно использовать для запроса нахождения учреждения, географического местоположения, или других точек вблизи заданной интересующей нас точки.

К сожалению, Google запрещает прямое использование этого API мобильными приложениями, и позволяет использовать его только из приложений сторонних серверов. Это означает, что для того, чтобы воспользоваться этой услугой, вы должны будете использовать свой собственный сервер для получения результатов поиска, и запросить этот сервер с вашего приложения iOS.

Так как сервер находится вне сферы деятельности для этого урока, вы можете найти готовый сервер в стартовом проекте, который написан на Dart и любезно предоставлен Бреттом Морганом, Ведущим Разработчиком в Google. С момента, как сервер стал с открытым исходным кодом, вы можете найти его на GitHub здесь.

Давайте рассмотрим шаги запуска сервера:

  1. Загружаем исходный код сервера.
  2. Чтобы установить Dart, вы будете использовать Homebrew. Homebrew это package manager для Mac, который позволяет простую установку различных packages. Если он еще у вас не установлен, откройте Terminal и запустите его:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

3. Когда Homebrew установлен, выполните следующую команду в Terminal:

brew tap dart-lang/dart

4. Установите Dart, запустив следующую команду в Terminal:

brew install dart

5. Затем найдите путь, по которому вы скачали код сервера, и откройте его в Terminal:

cd [PATH_TO_SERVER_CODE]

6. Установите сервисный код, выполнив следующее в Terminal:

pub install

7. И наконец, запустите сервер, используя следующую команду:

pub run bin/main.dart -k [YOUR_API_KEY] -p 10000

Замените YOUR_API_KEY северным ключом, который вы создали ранее.

Заметка

Для более подробной информации, проверьте файл readme.md в папкеplaces_api_key_proxy, которую вы загрузили на первом этапе.

Если вы все сделали правильно, вы должны увидеть окно Терминала таким:

Чтобы убедиться, что Google Places API работает, нажмите здесь. Вы должны увидеть результаты выполнения запроса в формате JSON. Если вы откроете окно вашего Терминала, то увидите несколько логов:

Заметка

Вы должны держать окно Терминала открытым, пока работает ваше приложение. Если вы его закрыли, пройдите наши 5 и 7 чтобы запустить сервер снова.

 

Заметка

Текущая конфигурация позволит вам запустить сервер из симулятора iPhone. Чтобы запустить его из устройства, найдите GoogleDataProvider.swift, затем найдите localhost и замените его на IP-адрес вашего компьютера Mac. Кроме того, убедитесь, что ваш Mac и устройство на iOS подключены одной и той же сети Wi-Fi.

Прежде чем вы вернетесь обратно к коду, есть еще один шаг, который нужно пройти. Вернитесь к проекту Feed Me. В Info.plist добавьте новое значение App Transport Security Settings (с учетом регистра), и как его подкласс, добавьте Allow Arbitrary Loads, и установите это значение на YES.

Заметка

Добавление этого ключа вашему Info.plist необходимо потому, что на iOS 9 ваше приложение будет предотвращать соединение с не-HTTPS серверами. В этом уроке, вы будете тестировать на локальном сервере без включения HTTPS, так что вы отключаете дефолтное поведение.

Теперь, когда вы можете запрашивать места из Google, давайте вернемся к коду.

Google Maps iOS SDK предоставляют вам класс GMSMarker для того, чтобы отмечать местоположения на карте. Каждый объект имеет координаты и изображение-иконку и переносит ее на карту, когда она добавляется.

Для этого приложения вам нужно будет еще дополнительная информацию о каждом маркере, так что вам нужно создать подкласс GMSMarker.

Создайте новый Cocoa Touch Class, назовите его PlaceMarker и сделать его подклассом GMSMarker. Убедитесь, что вы выбираете Swift в качестве языка этого файла.

Замените содержимое PlaceMarker.swift следующим:

class PlaceMarker: GMSMarker {
  // 1
  let place: GooglePlace
 
  // 2
  init(place: GooglePlace) {
    self.place = place
    super.init()
 
    position = place.coordinate
    icon = UIImage(named: place.placeType+"_pin")
    groundAnchor = CGPoint(x: 0.5, y: 1)
    appearAnimation = kGMSMarkerAnimationPop
  }
}

Это относительно простой кусок кода:

  1. Добавьте свойство типа GooglePlace для PlaceMarker.
  2. Объявите новый назначенный инициализатор, который принимает GooglePlace в качестве единственного параметра и полностью инициализирует PlaceMarker с позицией, иконку - изображение, якорь для позиции маркера и внешний вид анимации.

Затем добавьте еще два свойства MapViewController.swift таким образом:

let dataProvider = GoogleDataProvider()
let searchRadius: Double = 1000

Вы будете использовать dataProvider для вызов Google Places API, и searchRadius для определения, как далеко искать места от места расположения пользователя.

Добавьте следующий метод для MapViewController.swift:

func fetchNearbyPlaces(coordinate: CLLocationCoordinate2D) {
  // 1
  mapView.clear()
  // 2
  dataProvider.fetchPlacesNearCoordinate(coordinate, radius:searchRadius, types: searchedTypes) { places in
    for place: GooglePlace in places {
      // 3
      let marker = PlaceMarker(place: place)
      // 4
      marker.map = self.mapView
    }
  }
}

Давайте рассмотрим то, что вы сейчас только что добавили:

  1. Очистили карту всех маркеров.
  2. Использовали dataProvider для поиска Google близлежащих мест по searchRadius, с отфильтрованными выбранными критериями поиска пользователя.
  3. Перечислили через результаты, возвращаемые в завершающем замыкании и создали PlaceMarker для каждого результата.
  4. Установили карту маркеров. Эта строка кода - это то, что говорит карте перенести маркер.

Вот вопрос на $ 64,000 - когда вы хотите вызвать этот метод?

Во-первых, пользователь может ожидать, что сразу увидит места поблизости, когда приложение запустится.

Найдите locationManager(_:didUpdateLocations:) и добавьте следующую строку кода в конце, внутри выражения if let:

fetchNearbyPlaces(location.coordinate)

Во-вторых, у пользователя есть возможность изменять тип мест, которые будут отображаться на карте, так что вам нужно, чтобы произошло обновление результатов поиска, если выбранные типы изменились.

Найдите typesController(_:didSelectTypes:) и добавьте следующую строку кода в конце:

fetchNearbyPlaces(mapView.camera.target)

Наконец, вам нужно дать пользователю возможность выборки новых места, когда меняется их расположение.

Откройте Main.storyboard  и перетащите UIBarButtonItem из библиотеки объектов в левую часть навигационной панели MapViewController. Измените идентификатор кнопки на Refresh, как показано ниже:

Выберите Assistant Editor и CTRL + drag кнопки Refresh для MapViewController.swift. Выберите Action и назовите метод refreshPlaces. Замените содержимое вновь добавленного метода следующим:

@IBAction func refreshPlaces(sender: AnyObject) {
  fetchNearbyPlaces(mapView.camera.target)
}

Запустите ваш проект. Вы увидите контакты расположения выскакивают на карте. Измените тип поиска в TypesTableViewController, чтобы посмотреть, как меняются результаты:

Заметка

Если вы не видите, как появляется какое-либо из мест, то вы можете решить эту проблему, открыв GoogleDataProvider.swift и под строкой кода, который сериализует объект JSON, распечатать ошибку на консоль, добавив следующее: print(json["error_message"]).

Все эти маркеры добавят цвета на карту, но они не так много значит в использовании без дополнительной информации и должны предоставить дополнительные детали о месте пользователю.

Добавьте следующий метод для расширения GMSMapViewDelegate в MapViewController.swift

func mapView(mapView: GMSMapView!, markerInfoContents marker: GMSMarker!) -> UIView! {
  // 1
  let placeMarker = marker as! PlaceMarker
 
  // 2
  if let infoView = UIView.viewFromNibName("MarkerInfoView") as? MarkerInfoView {
    // 3
    infoView.nameLabel.text = placeMarker.place.name
 
    // 4
    if let photo = placeMarker.place.photo {
      infoView.placePhoto.image = photo
    } else {
      infoView.placePhoto.image = UIImage(named: "generic")
    }
 
    return infoView
  } else {
    return nil
  }
}

Этот метод вызывается каждый раз, когда пользователь нажимает на маркер на карте. Если вы вернете view, то оно всплывает над маркером. Если возвращается nil, то ничего не происходит. Как это произошло?

  1. Сначала вы сбрасываете отмеченный прикосновением по экрану маркер на PlaceMarker.
  2. Далее вы создаете MarkerInfoView из его nib. Класс MarkerInfoView является подклассом UIView, который идет вместе со стартовым проектом для этого туториала.
  3. Затем вы применяете название места для nameLabel.
  4. Проверьте, если есть фото для места. Если это так, добавьте фото на info view. Если нет, добавьте вместо нее универсального фотографию.

Прежде чем запустить приложение, вам нужно убедиться, что Отметка о местоположении не закрывает информационное окно. Добавьте следующий метод расширению GMSMapViewDelegate:

func mapView(mapView: GMSMapView!, didTapMarker marker: GMSMarker!) -> Bool {
  mapCenterPinImage.fadeOut(0.25)
  return false
}

Этот метод просто скрывает Отметку расположения, когда происходит прикосновение к маркеру. Метод возвращает false, чтобы указать, что вы не хотите переопределять поведение по умолчанию - центрировать карту вокруг маркера - при касании к маркеру.

Очевидно, что Отметка должна вновь появиться в какой-то момент. Добавьте следующее в конце mapView(_:willMove:):

if (gesture) {
  mapCenterPinImage.fadeIn(0.25)
  mapView.selectedMarker = nil
}

Это кусок кода проверяет, происходит ли движение от жеста пользователя. Если так, то он не скрывает Отметку местонахождение, используя метод fadeIn(_:). Установливая selectedMarker на nil удалит в настоящее время представленный infoView.

Наконец, добавьте следующий метод для расширения GMSMapViewDelegate:

func didTapMyLocationButtonForMapView(mapView: GMSMapView!) -> Bool {
  mapCenterPinImage.fadeIn(0.25)
  mapView.selectedMarker = nil
  return false
}

Этот метод начинает работать, когда пользователь нажимает кнопку Locate; карта будет центрирована вокруг местоположения пользователя. Возврат false показывает, что оно не изменяет дефолтное поведение при нажатии на кнопку.

Запустите ваше приложение; выберете маркер, и вы увидите как Отметка расположения исчезнет. Прокрутка карты закрывает infoView и возвращает Отметку обратно:

Ура, вы сделали это! Теперь у вас есть полностью функционирующее приложение с Google Maps. :]

Конечный проект можно найти здесь.

Что дальше?

Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!

Источник урока: http://www.raywenderlich.com/109888/google-maps-ios-sdk-tutorial#comments

Урок подготовил: Иван Акулов