iOS 앱에 지도 추가하기(Swift)

1. 시작하기 전에

이 Codelab에서는 Swift에서 Google Maps Platform을 사용하여 iOS 앱을 빌드하는 방법을 알아봅니다. 다음 작업을 처리하는 iOS 앱을 빌드하게 됩니다.

  • iOS용 Maps SDK와 iOS용 Maps SDK 유틸리티 라이브러리 로드
  • 오스트레일리아 시드니를 중심으로 하는 지도 표시
  • 시드니 주변의 100개 지점에 맞춤 마커 표시
  • 마커 클러스터링 구현
  • 마커가 탭되면 지도의 중심을 다시 설정하고 지도에 원을 그리는 사용자 상호작용 사용 설정

iOS 앱의 마커가 표시된 지도

사전 준비 사항

  • Swift 및 iOS 개발에 관한 기본 지식

과정 내용

  • iOS용 Maps SDK 및 iOS용 Google Maps SDK 유틸리티 라이브러리 로드
  • 지도 로드하기
  • 마커, 맞춤 마커, 마커 클러스터링 사용하기
  • 사용자 상호작용 제공을 위한 iOS용 Maps SDK 이벤트 시스템 작동하기
  • 프로그래매틱 방식으로 지도 카메라 제어하기
  • 지도에 그리기

필요한 사항

이 Codelab을 완료하려면 아래의 계정, 서비스 및 도구가 필요합니다.

  • Xcode 12.0 이상 혹은 타겟 SDK 12.0 이상
  • 설치 완료된 CocoaPods
  • 결제가 사용 설정된 Google Cloud Platform 계정(다음 단계 참고)
  • iOS용 Maps SDK가 사용 설정된 Cloud Console의 프로젝트(다음 단계 참고)

2. 설정하기

아래의 사용 설정 단계에서 iOS용 Maps SDK를 사용 설정해야 합니다.

Google Maps Platform 설정하기

Google Cloud Platform 계정 및 결제가 사용 설정된 프로젝트가 없는 경우 Google Maps Platform 시작하기 가이드를 참고하여 결제 계정 및 프로젝트를 만듭니다.

  1. Cloud Console에서 프로젝트 드롭다운 메뉴를 클릭하고 이 Codelab에 사용할 프로젝트를 선택합니다.

  1. Google Cloud Marketplace에서 이 Codelab에 필요한 Google Maps Platform API 및 SDK를 사용 설정합니다. 사용 설정을 위해 이 동영상 또는 이 문서에서 설명하고 있는 단계를 따르세요.
  2. Cloud Console의 사용자 인증 정보 페이지에서 API 키를 생성합니다. 이 동영상 또는 이 문서에서 설명하고 있는 단계를 따릅니다. Google Maps Platform에 대한 모든 요청에는 API 키가 필요합니다.

빠른 시작

빠르게 시작할 수 있도록 이 Codelab을 따라 하는 데 도움이 되는 시작 코드가 있습니다.

  1. git를 설치한 경우 저장소를 클론합니다.
git clone https://github.com/googlemaps/codelab-maps-platform-101-swift.git

또는 코드 받기를 클릭하여 소스 코드를 다운로드합니다.

  1. 코드를 다운로드한 후 /starter 디렉터리에서 StarterApp 프로젝트를 엽니다. 이 프로젝트에는 Codelab을 완료하는 데 필요한 기본 파일 구조가 포함되어 있습니다. 작업해야 하는 모든 항목은 /starter/StarterApp 디렉터리에 있습니다.

실행 중인 전체 솔루션 코드를 확인하려면 /solution/SolutionApp 디렉터리에서 완료된 코드를 확인하세요.

3. iOS용 Maps SDK 설치하기

iOS용 Maps SDK를 사용하려면 먼저 필요한 종속 항목을 설치해야 합니다. 설치는 두 단계로 구성되며, 먼저 Cocoapods 종속 항목 관리자에서 iOS용 Maps SDK와 iOS용 Maps SDK 유틸리티 라이브러리를 설치한 다음 SDK에 API 키를 제공하면 됩니다.

  1. iOS용 Maps SDK 및 iOS용 Maps SDK 유틸리티 라이브러리를 Podfile에 추가하세요.

본 Codelab에서는 Google 지도의 핵심 기능을 모두 제공하는 iOS용 Maps SDK와 지도를 보강하는 다양한 유틸리티(예: 마커 클러스터링)를 제공하는 지도 iOS 유틸리티 라이브러리를 모두 사용합니다.

시작하려면 Xcode(또는 선호하는 텍스트 편집기)에서 Podfile을 열고 iOS용 Maps SDK 및 유틸리티 라이브러리 종속 항목이 # Pods for StarterApp 댓글에 포함되도록 파일을 업데이트하세요.

pod 'GoogleMaps', '6.1.0'
pod 'Google-Maps-iOS-Utils', '3.4.0'

iOS용 Maps SDK 버전 문서에서 SDK의 최신 버전을 확인하고 유지관리 방법을 확인하세요.

Podfile은 다음과 같이 표시됩니다.

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '12.0'

target 'StarterApp' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for StarterApp
  pod 'GoogleMaps', '6.1.0'
  pod 'Google-Maps-iOS-Utils', '3.4.0'
end
  1. iOS용 Maps SDK 및 iOS용 Maps SDK 유틸리티 라이브러리 포드를 설치하세요.

종속 항목을 설치하려면 명령줄의 /starter 디렉터리에서 pod install을 실행하세요. CocoaPods는 자동으로 종속 항목을 다운로드하고 StarterApp.xcworkspace를 만듭니다.

  1. 종속 항목이 설치되면 /starter 디렉터리에서 open StarterApp.xcworkspace를 실행하여 Xcode에서 파일을 연 다음 Command+R을 눌러 iPhone 시뮬레이터에서 앱을 실행합니다. 설정이 제대로 되었다면 시뮬레이터가 실행되고 검은색 화면이 표시됩니다. 아직 아무것도 만들지 않았고 예정된 과정이니 걱정하지 않으셔도 됩니다.
  2. AppDelegate.swift에서 SDK를 가져옵니다.

종속 항목이 설치되었으므로 SDK에 API 키를 제공할 차례입니다. 먼저, import UIKit 가져오기 문 아래에 다음을 추가하여 iOS용 Maps SDK를 종속 항목으로 가져오세요.

import GoogleMaps
  1. application: didFinishLaunchingWithOptions:GMSServices에서 provideAPIKey를 호출하여 API 키를 iOS SDK에 전달합니다.
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    // Override point for customization after application launch.
    GMSServices.provideAPIKey("YOUR_API_KEY")

    return true
  }

업데이트된 AppDelegate.swift 파일은 다음과 같이 표시됩니다.

import UIKit
import GoogleMaps

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    // Override point for customization after application launch.
    GMSServices.provideAPIKey("YOUR_API_KEY")

    return true
  }

}

YOUR_API_KEY를 Cloud Console에서 만든 API 키로 바꿉니다.

이제 종속 항목이 설치되고 API 키가 제공되었으므로 iOS용 Maps SDK를 호출할 수 있습니다.

4. 지도 표시하기

첫 지도를 표시할 차례입니다.

iOS용 Maps SDK에서 가장 흔히 사용되는 부분은 GMSMapView 클래스입니다. 이 클래스에서는 지도 인스턴스를 생성 및 조작할 수 있는 많은 메서드가 제공됩니다. 아래의 과정을 따르세요.

  1. ViewController.swift를 엽니다.

여기에서 이 Codelab의 나머지 작업을 진행하게 됩니다. 뷰 컨트롤러의 loadViewviewDidLoad 수명 주기 이벤트가 이미 스텁 처리되고 있음을 확인합니다.

  1. 파일 상단에 다음을 추가하여 iOS용 Maps SDK를 가져옵니다.
import GoogleMaps
  1. GMSMapView를 저장할 ViewController 인스턴스 변수를 선언합니다.

GMSMapView의 인스턴스는 본 Codelab 전체 과정에서 사용할 기본 객체로 다양한 뷰 컨트롤러 수명 주기 메서드에서 이를 참조하고 그에 따라 조치를 취합니다. 객체 사용을 위해 이 객체를 저장하는 인스턴스 변수를 선언하도록 ViewController 구현을 업데이트하세요.

class ViewController: UIViewController {

  private var mapView: GMSMapView!

  ...
}
  1. loadView에서 GMSCameraPosition의 인스턴스를 만듭니다.

GMSCameraPosition은 지도의 중앙에 표시할 위치와 화면의 확대/축소 수준을 정의합니다. 이 코드는 cameraWithLatitude:longitude:zoom: 메서드를 호출하여 오스트레일리아 시드니의 위도 -33.86, 경도 151.20 지점을 지도 중앙에 확대/축소 수준 12로 표시합니다.

let camera:GMSCameraPosition = GMSCameraPosition.camera(withLatitude: -33.86, longitude: 151.20, zoom: 12)
  1. loadView에서 GMSMapView의 인스턴스를 만들어 지도를 인스턴스화하세요.

새 지도 인스턴스를 만들려면 GMSMapView(frame: CGRect, camera: GMSCameraPosition)를 호출하세요. 프레임이 CGRect.zero로 설정된 방식에 유의해야 합니다. 이 코드는 뷰 컨트롤러 내 (0,0) 지점의 너비 0, 높이 0 프레임을 지정하는 iOS CGGeometry 라이브러리에서 가져온 전역 변수입니다. 카메라는 조금 전에 생성한 카메라 위치로 설정됩니다.

그런 다음 지도를 표시하려면 뷰 컨트롤러의 루트 뷰를 mapView로 설정하세요. 그러면 지도가 전체 화면으로 표시됩니다.

    mapView = GMSMapView(frame: .zero, camera: camera)
    self.view = mapView
  1. GMSMapViewDelegate를 뷰 컨트롤러로 설정합니다.

지도뷰 대리자를 구현하면 GMSMapView 인스턴스에서 발생하는 사용자 상호작용의 이벤트를 처리할 수 있습니다. 이 과정은 나중에 필요합니다.

먼저, GMSMapViewDelegate:의 프로토콜을 준수하도록 ViewController의 인터페이스를 업데이트하세요.

class ViewController: UIViewController, GMSMapViewDelegate

그런 다음, loadView 함수에 다음을 추가하여 GMSMapViewDelegateViewController로 설정합니다.

    mapView.delegate = self

이제 iOS 시뮬레이터(Command+R)에서 앱을 새로고침하면 그림 1과 같이 지도가 표시됩니다.

Google 지도를 보여주는 iOS 앱

그림 1. Google 지도를 보여주는 iOS 앱

이상으로 GMSMapView의 인스턴스를 통해 오스트레일리아 시드니를 지도 중앙에 표시하는 방법에 대해 알아보았습니다.

이제 ViewController.swift 파일이 다음과 같이 표시됩니다.

import UIKit
import GoogleMaps

class ViewController: UIViewController, GMSMapViewDelegate {

  private var mapView: GMSMapView!

  override func loadView() {

    // Load the map at set latitude/longitude and zoom level
    let camera:GMSCameraPosition = GMSCameraPosition.camera(withLatitude: -33.86, longitude: 151.20, zoom: 11)

    mapView = GMSMapView(frame: .zero, camera: camera)
    self.view = mapView
    mapView.delegate = self
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
  }

}

5. 지도 스타일 지정하기(선택사항)

클라우드 기반 지도 스타일 지정을 사용하여 지도 스타일을 맞춤설정할 수 있습니다.

지도 ID 만들기

연결된 지도 스타일이 있는 지도 ID를 아직 만들지 않은 경우 지도 ID 가이드를 참고하여 다음 단계를 완료하세요.

  1. 지도 ID 만들기
  2. 지도 ID를 지도 스타일에 연결하기

앱에 지도 ID 추가하기

이전 단계에서 만든 지도 ID를 사용하려면 ViewController.swift 파일을 열고 loadView 메서드 내에서 GMSMapID 객체를 만들고 지도 ID를 제공합니다. 다음으로, GMSMapID 객체를 매개변수로 제공하여 GMSMapView 인스턴스화를 수정합니다.

ViewController.swift

  override func loadView() {

    // Load the map at set latitude/longitude and zoom level
    let camera:GMSCameraPosition = GMSCameraPosition.camera(withLatitude: -33.86, longitude: 151.20, zoom: 11)
    let mapID = GMSMapID(identifier: "YOUR_MAP_ID")

    mapView = GMSMapView(frame: .zero, mapID: mapID, camera: camera)
    self.view = mapView
    mapView.delegate = self
  }

그런 다음 앱을 실행하여 선택한 스타일로 지도를 확인합니다.

6. 지도에 마커 추가하기

개발자가 iOS용 Maps SDK를 많은 용도로 사용하고 있는데, 지도에 마커를 추가하는 경우가 가장 많습니다. 마커는 지도에 특정 지점을 표시하는 기능이며, 사용자 상호작용을 처리하는 일반적인 UI 요소입니다. Google 지도를 사용해 본 적이 있다면 그림 2의 빨간색 핀과 같은 기본 마커에 익숙할 것입니다.

빨간색 마커가 있는 지도

그림 2. 빨간색 마커가 있는 지도

이 단계에서는 GMSMarker 클래스를 이용해 지도에 마커를 넣는 방법을 보여줍니다.

마커는 뷰 컨트롤러의 loadView 수명 주기 이벤트에 있는 이전 단계에서 지도가 로드된 후에만 지도에 넣을 수 있습니다. 따라서 뷰와 지도가 로드된 후 호출되는 viewDidLoad 수명 주기 이벤트의 단계를 완료하시게 됩니다.

  1. CLLocationCoordinate2D 객체를 정의합니다.

CLLocationCoordinate2D는 iOS CoreLocation 라이브러리에서 제공하는 구조이며, 설정된 위도와 경도의 지리적 위치를 정의합니다. 첫 번째 마커를 만들려면 CLLocationCoordinate2D 객체를 정의하고 위도와 경도를 지도 중앙으로 설정하세요. camera.target.latitudecamera.target.longitude 속성을 사용하여 지도뷰에서 지도 중앙의 좌표에 액세스할 수 있습니다.

    // Add a single marker with a custom icon
    let mapCenter = CLLocationCoordinate2DMake(mapView.camera.target.latitude, mapView.camera.target.longitude)
  1. GMSMarker의 인스턴스를 생성합니다.

iOS용 Maps SDK는 GMSMarker 클래스를 제공합니다. GMSMarker의 각 인스턴스는 지도상의 개별 마커를 나타내며, markerWithPosition:을 호출하고 CLLocationCoordinate2D 객체를 전달하여 지도에 마커를 표시할 위치를 SDK에 알리면서 생성됩니다.

    let marker = GMSMarker(position: mapCenter)
  1. 맞춤 마커 아이콘을 설정합니다.

Google 지도의 기본 빨간색 핀 마커는 그 자체로 멋지지만, 이 마커를 맞춤설정하여 지도에 표시할 수도 있습니다. iOS용 Maps SDK에서는 맞춤 마커 사용이 매우 쉽습니다. StarterApp 프로젝트에 있는 'custom_pin.png'라는 이미지를 사용할 수 있으며, 다른 원하는 이미지를 사용해도 됩니다.

맞춤 마커를 설정하려면 마커의 icon 속성을 UIImage의 인스턴스로 설정하세요.

    marker.icon = UIImage(named: "custom_pin.png")
  1. 마커를 지도에 렌더링합니다.

마커가 생성되지만, 지도에 표시되지는 않습니다. 지도에 표시하려면 GMSMarker 인스턴스의 map 속성을 GMSMapView의 인스턴스로 설정하세요.

    marker.map = mapView

이제 앱을 새로고침하면 그림 3과 같이 지도에 마커가 표시됩니다.

가운데에 빨간색 마커가 있는 Google 지도가 표시된 iOS 앱

그림 3. 가운데에 빨간색 마커가 있는 Google 지도가 표시된 iOS 앱

이 섹션에서는 GMSMarker 클래스의 인스턴스를 생성하고 지도뷰에 적용하여 지도에 마커를 표시해 보았습니다. 이제 ViewController.swift의 업데이트된 viewDidLoad 수명 주기 이벤트가 다음과 같이 표시됩니다.

  override func viewDidLoad() {
    super.viewDidLoad()

    // Add a single marker with a custom icon
    let mapCenter = CLLocationCoordinate2DMake(mapView.camera.target.latitude, mapView.camera.target.longitude)
    let marker = GMSMarker(position: mapCenter)

    marker.icon = UIImage(named: "custom_pin.png")
    marker.map = mapView
  }

7. 마커 클러스터링 사용 설정하기

마커를 많이 사용하거나 여러 마커가 서로 가까이 있는 경우, 마커가 서로 겹치거나 복잡하게 보이면서 사용자 경험이 저하될 수 있습니다. 예를 들어 두 마커가 서로 너무 가까이 있으면 그림 4와 같이 표시될 수 있습니다.

두 마커가 서로 너무 가까움

그림 4. 두 마커가 서로 너무 가까움

이러한 경우에 마커 클러스터링이 사용됩니다. 마커 클러스터링은 서로 가까이 있는 여러 마커를 아이콘 하나로 그룹화하는 기능이며, 자주 사용됩니다. 그룹화의 정도는 화면의 확대/축소 수준에 따라 달라집니다. 그림 5를 참고하세요.

단일 아이콘으로 클러스터링된 마커의 예

그림 5. 단일 아이콘으로 클러스터링된 마커의 예

마커 클러스터링의 알고리즘은 지도에 표시되는 영역을 그리드로 나눈 다음 동일한 셀에 있는 아이콘을 클러스터링합니다. Google Maps Platform팀에서 iOS용 Google Maps SDK 유틸리티 라이브러리라는, 자동으로 마커 클러스터링을 처리하는 유용한 오픈소스 유틸리티 라이브러리를 만들었습니다. Google Maps Platform 문서에서 마커 클러스터링에 대해 자세히 알아보거나 GitHub에서 iOS 유틸리티 라이브러리의 소스를 확인해 보세요.

  1. 지도에 마커를 많이 추가합니다.

마커 클러스터링이 어떻게 작동하는지를 제대로 확인하려면 지도에 마커가 많아야 합니다. 마커를 쉽게 추가할 수 있도록 MarkerGenerator.swift의 스타터 프로젝트에서 편리한 마커 생성기가 제공됩니다.

지도에 지정된 수의 마커를 추가하려면 이전 단계의 코드 아래에 있는 뷰 컨트롤러의 viewDidLoad 수명 주기에서 MarkerGenerator(near:count:).markerArray를 호출하세요. 이 메서드는 CLLocationCoordinate2D 객체에 지정된 좌표 주변의 무작위 위치에 count에서 지정된 수만큼의 마커를 생성합니다. 이때 앞에서 만든 mapCenter 변수를 전달하면 마커가 [GMSMarker]로 반환됩니다.

    // Generate many markers
    let markerArray = MarkerGenerator(near: mapCenter, count: 100).markerArray

markerArray의 정의 뒤에 다음과 같은 행을 추가한 다음, 앱을 실행하여 이 여러 마커가 어떻게 표시되는지 테스트할 수 있습니다. 마커 클러스터링을 사용하여 마커 표시를 대신 관리하는 다음 단계로 넘어가기 전에 다음 행을 주석 처리하세요.

    // Comment the following code out if using the marker clusterer
    // to manage markers instead.
    for marker in markerArray {
      marker.map = mapView
    }
  1. iOS용 Google Maps SDK 유틸리티 라이브러리를 가져옵니다.

지도 iOS 유틸리티 라이브러리를 프로젝트의 종속 항목으로 추가하려면 ViewController.swift 상단의 종속 항목 목록에 다음을 추가하세요.

import GoogleMapsUtils
  1. 마커 클러스터러를 구성합니다.

마커 클러스터러를 사용하려면 이 기능의 작동 방식을 구성하기 위한 클러스터링 알고리즘, 아이콘 생성기, 렌더기를 제공해야 합니다. 알고리즘은 마커 클러스터링 방식(예: 동일한 클러스터에 넣을 마커 간의 거리)을 결정합니다. 아이콘 생성기는 다양한 확대/축소 수준에서 사용할 클러스터 아이콘을 제공합니다. 렌더기는 지도에서 클러스터 아이콘의 실제 렌더링을 처리합니다.

원할 경우 이를 모두 처음부터 작성해도 되지만, 손쉽게 처리할 수 있도록 지도 iOS 유틸리티 라이브러리에서 기본 구현을 제공합니다. 다음을 추가하세요.

    // Set up the cluster manager with a supplied icon generator and renderer.
    let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
    let iconGenerator = GMUDefaultClusterIconGenerator()
    let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
  1. GMUClusterManager의 인스턴스를 생성하세요.

GMUClusterManager는 개발자가 지정한 알고리즘, 아이콘 생성기, 렌더기를 사용하여 마커 클러스터링을 구현하는 클래스입니다. 렌더기를 만들고 지도뷰에서 사용할 수 있게 하려면 먼저 ViewController 구현에 인스턴스 변수를 추가하여 클러스터 관리자 인스턴스를 저장하세요.

class ViewController: UIViewController, GMSMapViewDelegate {

  private var mapView: GMSMapView!
  private var clusterManager: GMUClusterManager!
}

그런 다음, viewDidLoad 수명 주기 이벤트에서 GMUClusterManager의 인스턴스를 생성하세요.

    clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
  1. 마커를 추가하고 마커 클러스터러를 실행합니다.

이제 마커 클러스터러 인스턴스가 구성되었으므로 add(items:)를 호출하여 클러스터링할 마커 배열을 클러스터 관리자에 전달한 다음 cluster를 호출하여 클러스터러를 실행하세요.

    clusterManager.setMapDelegate(self)
    clusterManager.add(markerArray)
    clusterManager.cluster()

앱을 새로고침하면 그림 6의 예와 같이 많은 마커가 깔끔하게 클러스터링된 것을 볼 수 있습니다. 지도에서 손가락을 벌리거나 모으면서 화면에 다양한 수준의 확대/축소를 적용하고 마커 클러스터가 제대로 조정되는지 확인하세요.

Google 지도와 클러스터링된 마커가 있는 iOS 앱

그림 6. Google 지도와 클러스터링된 마커가 있는 iOS 앱

이상으로 iOS용 Google Maps SDK 유틸리티 라이브러리에 있는 마커 클러스터러의 인스턴스를 구성한 다음 이를 이용해 지도에서 100개의 마커를 클러스터링해 보았습니다. 이제 ViewController.swiftviewDidLoad 수명 주기 이벤트가 다음과 같이 표시됩니다.

  override func viewDidLoad() {
    super.viewDidLoad()

    // Add a single marker with a custom icon
    let mapCenter = CLLocationCoordinate2DMake(mapView.camera.target.latitude, mapView.camera.target.longitude)
    let marker = GMSMarker(position: mapCenter)

    marker.icon = UIImage(named: "custom_pin.png")
    marker.map = mapView

    // Generate many markers
    let markerArray = MarkerGenerator(near: mapCenter, count: 100).markerArray
    // Comment the following code out if using the marker clusterer
    // to manage markers instead.
    //    for marker in markerArray {
    //      marker.map = mapView
    //    }

    // Set up the cluster manager with a supplied icon generator and renderer.
    let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
    let iconGenerator = GMUDefaultClusterIconGenerator()
    let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
    clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)

    clusterManager.setMapDelegate(self)
    clusterManager.add(markerArray)
    clusterManager.cluster()
  }

8. 사용자 상호작용 추가하기

여러 마커를 표시하고 마커 클러스터링이 사용되는 깔끔한 지도를 만들었습니다. 이번 단계에서는 지도의 사용자 경험 개선을 위해 앞서 뷰 컨트롤러에 설정한 GMSMapViewDelegate를 이용해 사용자 상호작용 처리를 추가해 보겠습니다.

iOS용 Maps SDK는 지도뷰 대리자(다양한 사용자 상호작용이 발생할 때 코드를 실행할 수 있게 해주는 이벤트 핸들러 포함)를 통해 구현된 포괄적인 이벤트 시스템을 제공합니다. 예를 들어 지도뷰 대리자에는 사용자의 지도 및 마커 클릭, 지도뷰 이동, 확대/축소 등의 상호작용을 위한 코드 실행을 트리거할 수 있게 해주는 메서드가 포함됩니다.

이 단계에서는 사용자가 탭한 마커를 프로그래매틱 방식으로 지도 화면의 중앙에 표시합니다.

  1. 마커 탭 리스너를 구현합니다.

사용자가 앞서 생성된 마커 중 하나를 탭할 때 또는 마커 클러스터를 탭할 때마다 mapView(_:didTap:)가 호출됩니다(내부적으로 마커 클러스터는 GMSMarker의 인스턴스로 구현됨).

이벤트 리스너를 구현하려면 먼저 ViewController.swift 하단에서 닫는 중괄호 전까지 스텁 처리하세요.

  func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {

    return false
  }

메서드에서 false를 반환합니다. 이렇게 하면 기본 GMSMarker 기능(예: 구성된 경우, 이벤트 핸들러 코드 실행 후 정보 창 표시)을 계속 실행하라고 iOS SDK에 지시하게 됩니다.

  1. 마커 또는 마커 클러스트를 탭하면 탭 이벤트를 처리하고 지도 중앙이 재설정되도록 카메라를 이동합니다.

mapView(_:didTap:)는 호출될 경우 탭한 GMSMarker의 인스턴스를 전달하므로 코드에서 이를 처리할 수 있게 됩니다. 이 인스턴스를 이용해 지도의 중앙을 재설정할 수 있는데, 이벤트 핸들러 내에서 지도뷰의 animate(toLocation:)를 호출하고 position 속성에서 마커 인스턴스의 위치를 전달하면 됩니다.

    // Animate to the marker
    mapView.animate(toLocation: marker.position)
  1. 마커 클러스터는 탭하면 확대됩니다.

일반적인 UX 패턴에서는 마커 클러스터를 탭했을 때 클러스터가 확대됩니다. 이렇게 되면 화면 확대/축소 수준이 낮을 때 클러스터가 확대되므로 사용자가 클러스터링된 마커를 볼 수 있습니다.

앞서 언급했듯이 마커 클러스터 아이콘은 실제로 맞춤 아이콘을 이용해 GMSMarker를 구현한 것입니다. 마커 또는 마커 클러스터를 탭했는지는 어떻게 알 수 있을까요? 마커 클러스터러 관리자가 새 클러스터 아이콘을 만들 때 GMSMarker의 인스턴스를 구현하여 GMUCluster. 프로토콜을 준수합니다. 이벤트 핸들러에 전달된 마커가 이 프로토콜을 준수하는지는 조건을 이용해 확인할 수 있습니다.

클러스터를 탭한 것을 프로그래매틱 방식으로 파악했으면 지도뷰 인스턴스에서 animate(toZoom:)을 호출하고 확대/축소를 현재보다 한 단계 높은 수준으로 설정하세요. 현재의 확대/축소 수준은 camera.zoom 속성의 지도뷰 인스턴스에서 사용할 수 있습니다.

아래의 코드가 어떻게 true를 반환하는지도 확인하세요. 이러한 반환이 있으면 이벤트 처리를 완료했고 핸들러에서 추가 코드를 실행하지 않을 것임을 이벤트 핸들러에 알립니다. 이렇게 하는 이유 중 하나는 기본 GMSMarker 객체가 기본 동작의 나머지를 실행하지 못하게 하기 위함입니다. 예를 들어 클러스터 아이콘을 탭할 경우에는 정보 창 표시가 적절하지 않습니다.

    // If the tap was on a marker cluster, zoom in on the cluster
    if let _ = marker.userData as? GMUCluster {
      mapView.animate(toZoom: mapView.camera.zoom + 1)
      return true
    }

앱을 새로고침하고 마커와 마커 클러스터 몇 개를 탭해 보세요. 마커나 마커 클러스터를 탭하면 해당 요소가 지도의 중앙에 표시됩니다. 마커 클러스터를 탭하면 지도가 한 단계씩 확대되고 마커 클러스터가 커지면서 클러스터링된 마커가 표시됩니다.

이상으로 마커 탭 리스너를 구현해 보았으며, 탭한 요소를 지도의 중앙에 표시하고 이 요소가 마커 클러스터 아이콘인 경우 확대하는 이벤트를 처리하는 방법도 살펴봤습니다.

mapView(_:didTap:) 메서드가 다음과 같이 표시됩니다.

  func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {

    // Animate to the marker
    mapView.animate(toLocation: marker.position)

    // If the tap was on a marker cluster, zoom in on the cluster
    if let _ = marker.userData as? GMUCluster {
      mapView.animate(toZoom: mapView.camera.zoom + 1)
      return true
    }

    return false
  }

9. 지도에 그리기

지금까지 100개의 무작위 지점에 마커를 표시하고 사용자 상호작용을 처리하는 시드니 지도를 만들었습니다. 이번 Codelab의 마지막 단계에서는 iOS용 Maps SDK의 그리기 기능을 이용해 지도 환경에 유용한 기능을 추가합니다.

시드니 관광객이 이용하는 지도라면 마커를 클릭할 때 주변 반경을 시각적으로 보여주는 기능이 유용할 것입니다. 이 기능이 있으면 클릭한 마커 위치에서 무난하게 걸어갈 수 있는 지점을 쉽게 찾을 수 있습니다.

iOS SDK에는 지도에 정사각형 및 기타 다각형, 선, 원 등을 그릴 수 있는 여러 기능이 있습니다. 이 단계에서는 마커가 클릭되면 마커 주위에 반경 800미터(약 0.5마일)가 표시되도록 원을 렌더링합니다.

  1. ViewController 구현에 circle 인스턴스 변수를 추가합니다.

이 인스턴스 변수는 가장 최근에 그려진 원을 저장하는 데 사용되며, 다른 원이 그려지기 전에 기존 원은 삭제됩니다. 사실 사용자에게는 별다른 도움이 되지 않는 기능입니다. 마커를 탭할 때마다 반경을 나타내는 원이 표시되면 번잡한 느낌을 줄 수 있습니다.

이 문제를 해결하기 위해 다음과 같이 ViewController 구현을 업데이트하세요.

class ViewController: UIViewController, GMSMapViewDelegate {

  private var mapView: GMSMapView!
  private var clusterManager: GMUClusterManager!
  private var circle: GMSCircle? = nil
  ...
}
  1. 마커를 탭하면 원이 그려집니다.

mapView(_:didTap:) 메서드 하단에서 return false 문 바로 위에 다음 코드를 추가하여 iOS SDK에 있는 GMSCircle 클래스의 인스턴스를 만드세요. GMSCircle(position:radius:)를 호출하고 탭한 마커의 위치를 전달하여 800미터 반경 원을 새로 그리는 인스턴스이며, 앞서 지도의 중앙을 재설정할 때의 방법과 동일합니다.

    // Draw a new circle around the tapped marker
    circle = GMSCircle(position: marker.position, radius: 800)
  1. 원의 스타일을 지정합니다.

기본적으로 GMSCircle은 검은색 획과 투명 채움이 적용된 원을 그립니다. 이렇게 하면 반경이 표시되지만 깔끔하지 않아 보기에 불편할 수 있습니다. 그런 다음, 스타일이 깔끔해지도록 원의 fillColor 속성에 UIColor를 할당하여 원의 채움 색상을 지정하세요. 아래의 코드는 투명도가 50%인 회색 채움을 적용합니다.

    circle?.fillColor = UIColor(red: 0.67, green: 0.67, blue: 0.67, alpha: 0.5)
  1. 지도에서 원을 렌더링합니다.

앞서 마커를 만들 때처럼, GMSCircle의 인스턴스를 만드는 것만으로는 충분하지 않습니다. 지도뷰 인스턴스를 원의 map 속성에 할당하세요.

    circle?.map = mapView
  1. 이전에 렌더링된 원은 삭제합니다.

앞서 언급했듯이, 지도에 원이 계속 추가되면 사용자 경험이 저해될 수 있습니다. 이전 탭 이벤트에서 렌더링된 원을 삭제하려면 circlemap 속성을 mapView(_:didTap:) 상단에서 nil로 설정하세요.

    // Clear previous circles
    circle?.map = nil

앱을 새로고침하고 마커를 탭하세요. 마커를 탭할 때마다 그림 7과 같이 새 원이 그려지고, 이전에 렌더링된 원은 삭제됩니다.

탭된 마커 주위에 그려진 원

그림 7. 탭된 마커 주위에 그려진 원

이상으로 마커를 탭할 때마다 원을 렌더링하는 GMSCircle 클래스를 사용해 보았습니다.

mapView(_:didTap:) 메서드는 다음과 같이 표시됩니다.

  func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {

    // Clear previous circles
    circle?.map = nil

    // Animate to the marker
    mapView.animate(toLocation: marker.position)

    // If the tap was on a marker cluster, zoom in on the cluster
    if let _ = marker.userData as? GMUCluster {
      mapView.animate(toZoom: mapView.camera.zoom + 1)
      return true
    }

    // Draw a new circle around the tapped marker
    circle = GMSCircle(position: marker.position, radius: 800)
    circle?.fillColor = UIColor(red: 0.67, green: 0.67, blue: 0.67, alpha: 0.5)
    circle?.map = mapView
    return false
  }

10. 축하합니다

대화형 Google 지도를 표시하는 iOS 앱을 성공적으로 빌드했습니다.

학습한 내용

다음 단계

  • 더 많은 아이디어를 얻으려면 샘플 및 데모의 maps-sdk-for-ios-samples GitHub 저장소를 탐색하거나 포크하세요.
  • Google Maps Platform으로 iOS 앱을 빌드하는 방법은 더 많은 Swift Codelab을 참고하세요.
  • 아래 질문에 답하여 Google에서 가장 유용한 콘텐츠를 만들 수 있도록 도와주세요.

다른 Codelab에서 어떤 내용을 다뤘으면 하시나요?

지도에서의 데이터 시각화 내 지도의 스타일 맞춤설정에 관한 추가 정보 지도에서 3D 상호작용 만들기

원하는 Codelab이 위에 나와 있지 않나요? 여기에서 새로운 문제로 요청하세요.