1. 시작하기 전에
이 Codelab에서는 iOS용 Maps 3D SDK를 사용하여 SwiftUI에서 3D 지도 앱을 만드는 방법을 알아봅니다.
학습 내용
- 카메라를 제어하여 위치를 확인하고 지도 주위를 날아다니는 방법
- 마커 및 모델을 추가하는 방법
- 선 및 다각형을 그리는 방법
- 장소 마커에서 사용자 클릭을 처리하는 방법
기본 요건
- 결제가 사용 설정된 Google Console 프로젝트
- API 키(선택적으로 iOS용 지도 3D SDK로 제한됨)
- SwiftUI를 사용한 iOS 개발에 관한 기본 지식
실습할 내용
- Xcode를 설정하고 Swift Package Manager를 사용하여 SDK 가져오기
- API 키를 사용하도록 앱 구성
- 앱에 기본 3D 지도 추가
- 카메라를 제어하여 특정 위치로 이동하고 해당 위치를 둘러봅니다.
- 지도에 마커, 선, 다각형, 모델 추가하기
필요한 항목
- Xcode 15 이상
2. 설정
다음 사용 설정 단계를 진행하려면 iOS용 Maps 3D SDK를 사용 설정해야 합니다.
Google Maps Platform 설정하기
Google Cloud Platform 계정 및 결제가 사용 설정된 프로젝트가 없는 경우 Google Maps Platform 시작하기 가이드를 참고하여 결제 계정 및 프로젝트를 만듭니다.
- Cloud Console에서 프로젝트 드롭다운 메뉴를 클릭하고 이 Codelab에 사용할 프로젝트를 선택합니다.
- Google Cloud Marketplace에서 이 Codelab에 필요한 Google Maps Platform API 및 SDK를 사용 설정합니다. 사용 설정을 위해 이 동영상 또는 이 문서에서 설명하고 있는 단계를 따르세요.
- Cloud Console의 사용자 인증 정보 페이지에서 API 키를 생성합니다. 이 동영상 또는 이 문서에서 설명하고 있는 단계를 따릅니다. Google Maps Platform에 대한 모든 요청에는 API 키가 필요합니다.
iOS용 지도 3D SDK 사용 설정
콘솔에서 Google Maps Platform > API 및 서비스 메뉴 링크를 사용하여 iOS용 지도 3D SDK를 찾을 수 있습니다.
사용 설정을 클릭하여 선택한 프로젝트에서 API를 사용 설정합니다.
3. 기본 SwiftUI 앱 만들기
참고: 각 단계의 솔루션 코드는 GitHub의 Codelab 샘플 앱 저장소에서 확인할 수 있습니다 .
Xcode에서 새 앱을 만듭니다.
이 단계의 코드는 GitHub의 GoogleMaps3DDemo 폴더에서 확인할 수 있습니다.
Xcode를 열고 새 앱을 만듭니다. SwiftUI를 지정합니다.
패키지 이름이 com.example.GoogleMaps3DDemo
인 앱 GoogleMaps3DDemo
를 호출합니다.
프로젝트에 GoogleMaps3D 라이브러리 가져오기
Swift Package Manager를 사용하여 프로젝트에 SDK를 추가합니다.
Xcode 프로젝트 또는 작업공간에서 File(파일) > Add Package Dependencies(패키지 종속 항목 추가)로 이동합니다. URL로 https://github.com/googlemaps/ios-maps-3d-sdk를 입력하고 Enter 키를 눌러 패키지를 가져온 다음 '패키지 추가'를 클릭합니다.
'Choose Package Products'(패키지 제품 선택) 창에서 지정된 기본 타겟에 GoogleMaps3D
가 추가되는지 확인합니다. 완료되면 '패키지 추가'를 클릭합니다.
설치를 확인하려면 타겟의 일반 창으로 이동합니다. 프레임워크, 라이브러리, 삽입된 콘텐츠에 설치된 패키지가 표시됩니다. 프로젝트 탐색기의 패키지 종속 항목 섹션을 확인하여 패키지와 버전을 확인할 수도 있습니다.
API 키 추가하기
API 키를 앱에 하드 코딩할 수도 있지만 이는 권장되지 않습니다. 구성 파일을 추가하면 API 키를 비밀로 유지하고 소스 제어에 체크인하지 않아도 됩니다.
프로젝트 루트 폴더에 새 구성 파일 만들기
Xcode에서 프로젝트 탐색기 창을 보고 있는지 확인합니다. 프로젝트 루트를 마우스 오른쪽 버튼으로 클릭하고 '템플릿에서 새 파일'을 선택합니다. '구성 설정 파일'이 표시될 때까지 스크롤합니다. 이 옵션을 선택하고 '다음'을 클릭합니다. 파일 이름을 Config.xcconfig
로 지정하고 프로젝트 루트 폴더가 선택되어 있는지 확인합니다. '만들기'를 클릭하여 파일을 만듭니다.
편집기에서 구성 파일에 다음과 같이 줄을 추가합니다. MAPS_API_KEY = YOUR_API_KEY
YOUR_API_KEY
를 내 API 키로 바꿉니다.
이 설정을 Info.plist
에 추가합니다.
이렇게 하려면 프로젝트 루트를 선택하고 '정보' 탭을 클릭합니다.
값이 $(MAPS_API_KEY)
인 MAPS_API_KEY
라는 새 속성을 추가합니다.
샘플 앱 코드에는 이 속성을 지정하는 Info.plist
파일이 있습니다.
지도 추가
GoogleMaps3DDemoApp.swift
파일을 엽니다. 앱의 진입점 및 기본 탐색입니다.
Hello World 메시지를 표시하는 ContentView()
를 호출합니다.
편집기에서 ContentView.swift
를 엽니다.
GoogleMaps3D
의 import
문을 추가합니다.
var body: some View {}
코드 블록 내의 코드를 삭제합니다. body
내에서 새 Map()
를 선언합니다.
Map
를 초기화하는 데 필요한 최소 구성은 MapMode
입니다. 이 값에는 두 가지 값이 가능합니다.
.hybrid
- 도로 및 라벨이 있는 위성 이미지.satellite
- 위성 이미지만.
.hybrid
에 문의하세요.
ContentView.swift
파일은 다음과 같습니다.
import GoogleMaps3D
import SwiftUI
@main
struct ContentView: View {
var body: some View {
Map(mode: .hybrid)
}
}
API 키를 설정합니다.
지도 초기화 전에 API 키를 설정해야 합니다.
지도가 포함된 View
의 init()
이벤트 핸들러에서 Map.apiKey
를 설정하면 됩니다. ContentView()
를 호출하기 전에 GoogleMaps3DDemoApp.swift
에서 설정할 수도 있습니다.
GoogleMaps3DDemoApp.swift
에서 WindowGroup
의 onAppear
이벤트 핸들러에 Map.apiKey
를 설정합니다.
구성 파일에서 API 키 가져오기
Bundle.main.infoDictionary
를 사용하여 구성 파일에서 만든 MAPS_API_KEY
설정에 액세스합니다.
import GoogleMaps3D
import SwiftUI
@main
struct GoogleMaps3DDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.onAppear {
guard let infoDictionary: [String: Any] = Bundle.main.infoDictionary else {
fatalError("Info.plist not found")
}
guard let apiKey: String = infoDictionary["MAPS_API_KEY"] as? String else {
fatalError("MAPS_API_KEY not set in Info.plist")
}
Map.apiKey = apiKey
}
}
}
앱을 빌드하고 실행하여 올바르게 로드되는지 확인합니다. 지구 지도가 표시됩니다.
4. 카메라를 사용하여 지도 뷰 제어
카메라 상태 객체 만들기
3D 지도 뷰는 Camera
클래스에 의해 제어됩니다. 이 단계에서는 위치, 고도, 방향, 기울기, 롤, 범위를 지정하여 지도 보기를 맞춤설정하는 방법을 알아봅니다.
카메라 설정을 저장하는 Helpers 클래스 만들기
MapHelpers.swift
라는 새 빈 파일을 추가합니다. 새 파일에서 GoogleMaps3D
를 가져오고 Camera
클래스에 확장 프로그램을 추가합니다. sanFrancisco
라는 변수를 추가합니다. 이 변수를 새 Camera
객체로 초기화합니다. latitude: 37.39, longitude: -122.08
에서 카메라를 찾습니다.
import GoogleMaps3D
extension Camera {
public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}
앱에 새 뷰 추가
CameraDemo.swift
라는 새 파일을 만듭니다. 새 SwiftUI 뷰의 기본 개요를 파일에 추가합니다.
Camera
유형의 camera
라는 @State
변수를 추가합니다. 방금 정의한 sanFrancisco
카메라로 초기화합니다.
@State
를 사용하면 지도를 카메라 상태에 바인딩하고 정보 소스로 사용할 수 있습니다.
@State var camera: Camera = .sanFrancisco
camera
속성을 포함하도록 Map()
함수 호출을 변경합니다. 카메라 상태 결합 $camera
를 사용하여 camera
속성을 카메라 @State
객체 (.sanFrancisco
)로 초기화합니다.
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
앱에 기본 탐색 UI 추가
기본 앱 진입점 GoogleMaps3DDemoApp.swift
에 NavigationView
를 추가합니다.
이렇게 하면 사용자가 데모 목록을 확인하고 각 데모를 클릭하여 열 수 있습니다.
GoogleMaps3DDemoApp.swift
을 수정하여 새 NavigationView
를 추가합니다.
NavigationLink
선언 2개가 포함된 List
를 추가합니다.
첫 번째 NavigationLink
는 Text
설명 Basic Map
과 함께 ContentView()
를 열어야 합니다.
두 번째 NavigationLink
는 CameraDemo()
를 열어야 합니다.
...
NavigationView {
List {
NavigationLink(destination: ContentView()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
}
}
...
Xcode 미리보기 추가
미리보기는 앱을 변경할 때 앱을 보고 상호작용할 수 있는 강력한 Xcode 기능입니다.
미리보기를 추가하려면 CameraDemo.swift
를 엽니다. struct
외부에 #Preview {}
코드 블록을 추가합니다.
#Preview {
CameraDemo()
}
Xcode에서 미리보기 창을 열거나 새로고침합니다. 지도에 샌프란시스코가 표시됩니다.
맞춤 3D 보기 설정
카메라를 제어하기 위해 추가 매개변수를 지정할 수 있습니다.
heading
: 카메라를 향하는 방위로, 북쪽에서부터 도 단위로 지정됩니다.tilt
: 기울기 각도(도)로, 0은 바로 머리 위를 향하고 90은 수평을 향합니다.roll
: 카메라의 수직 평면을 중심으로 롤하는 각도(도)range
: 위도, 경도 위치에서 카메라까지의 거리(미터)입니다.altitude
: 해수면 위의 카메라 높이입니다.
이러한 추가 매개변수를 제공하지 않으면 기본값이 사용됩니다.
카메라 뷰에 더 많은 3D 데이터를 표시하려면 더 가까운 기울어진 뷰를 표시하도록 초기 매개변수를 설정합니다.
altitude
, heading
, tilt
, roll
, range
값을 포함하도록 MapHelpers.swift
에서 정의한 Camera
를 수정합니다.
public static var sanFrancisco: Camera = .init(
latitude: 37.7845812,
longitude: -122.3660241,
altitude: 585,
heading: 288.0,
tilt: 75.0,
roll: 0.0,
range: 100)
앱을 빌드하고 실행하여 새로운 3D 뷰를 보고 살펴봅니다.
5. 기본 카메라 애니메이션
지금까지 카메라를 사용하여 기울기, 고도, 방위, 범위가 있는 단일 위치를 지정했습니다. 이 단계에서는 이러한 속성을 초기 상태에서 새 상태로 애니메이션하여 카메라 뷰를 이동하는 방법을 알아봅니다.
특정 위치로 이동
Map.flyCameraTo()
메서드를 사용하여 카메라를 초기 위치에서 새 위치로 애니메이션 처리합니다.
flyCameraTo()
메서드는 여러 매개변수를 사용합니다.
- 종료 위치를 나타내는
Camera
duration
: 애니메이션이 실행되는 시간(초)입니다.trigger
: 상태가 변경될 때 애니메이션을 트리거하는 관찰 가능한 객체입니다.completion
: 애니메이션이 완료될 때 실행되는 코드입니다.
이동할 위치 정의
MapHelpers.swift
파일을 엽니다.
시애틀을 표시할 새 카메라 객체를 정의합니다.
public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)
애니메이션을 트리거하는 버튼을 추가합니다.
CameraDemo.swift
를 엽니다. struct
내에서 새 불리언 변수를 선언합니다.
초깃값이 false
인 animate
를 호출합니다.
@State private var animate: Bool = false
VStack
아래에 Button
를 추가합니다. Button
가 지도 애니메이션을 시작합니다.
Button
에 '비행 시작'과 같은 적절한 Text
를 제공합니다.
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera:Camera = .sanFrancisco
@State private var animate: Bool = false
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
Button("Start Flying") {
}
}
}
}
Button 닫기에서 animate
변수의 상태를 전환하는 코드를 추가합니다.
Button("Start Flying") {
animate.toggle()
}
애니메이션을 시작합니다.
animate
변수의 상태가 변경될 때 flyCameraTo()
애니메이션을 트리거하는 코드를 추가합니다.
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
특정 위치 주변을 비행하기
Map.flyCameraAround()
메서드를 사용하여 위치를 둘러볼 수 있습니다. 이 메서드에는 다음과 같은 여러 매개변수가 사용됩니다.
- 위치와 뷰를 정의하는
Camera
duration
초입니다.rounds
: 애니메이션을 반복할 횟수입니다.trigger
: 애니메이션을 트리거하는 관찰 가능한 객체입니다.callback
: 애니메이션이 실행될 때 실행되는 코드입니다.
flyAround
이라는 새 @State
변수를 정의하고 초깃값을 false
로 설정합니다.
완료되면 flyCameraTo()
메서드 호출 바로 뒤에 flyCameraAround()
호출을 추가합니다.
뷰가 원활하게 변경되도록 둘러보기 시간은 비교적 길어야 합니다.
flyCameraTo()
가 완료되면 트리거 객체의 상태를 변경하여 flyCameraAround()
애니메이션을 트리거해야 합니다.
코드는 다음과 같이 표시됩니다.
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera:Camera = .sanFrancisco
@State private var animate: Bool = false
@State private var flyAround: Bool = false
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { flyAround = true }
)
.flyCameraAround(
.seattle,
duration: 15,
rounds: 0.5,
trigger: flyAround,
callback: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
}
#Preview {
CameraDemo()
}
앱을 미리 보거나 실행하여 flyCameraTo()
애니메이션이 완료되면 카메라가 대상 주위를 날아다니는 것을 확인합니다.
6. 지도에 마커를 추가합니다.
이 단계에서는 지도에 마커 핀을 그리는 방법을 알아봅니다.
Marker
객체를 만들어 지도에 추가합니다. SDK는 마커에 기본 아이콘을 사용합니다. 마지막으로 마커 고도 및 기타 속성을 조정하여 표시 방식을 변경합니다.
마커 데모를 위한 새 SwiftUI 뷰를 만듭니다.
프로젝트에 새 Swift 파일을 추가합니다. 이름을 MarkerDemo.swift
로 지정합니다.
SwiftUI 뷰의 윤곽을 추가하고 CameraDemo
에서와 같이 지도를 초기화합니다.
import SwiftUI
import GoogleMaps3D
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
마커 객체 초기화
mapMarker
라는 새 마커 변수를 선언합니다. MarkerDemo.swift
의 struct
코드 블록 상단
camera
선언 아래 줄에 정의를 배치합니다. 이 샘플 코드는 사용 가능한 모든 속성을 초기화합니다.
@State var mapMarker: Marker = .init(
position: .init(
latitude: 37.8044862,
longitude: -122.4301493,
altitude: 0.0),
altitudeMode: .absolute,
collisionBehavior: .required,
extruded: false,
drawsWhenOccluded: true,
sizePreserved: true,
zIndex: 0,
label: "Test"
)
지도에 마커를 추가합니다.
마커를 그리려면 지도 생성 시 호출되는 폐쇄에 마커를 추가합니다.
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
mapMarker
}
}
}
}
대상이 MarkerDemo()
이고 Text
이 '마커 데모'라고 설명하는 새 NavigationLink
를 GoogleMaps3DDemoApp.swift
에 추가합니다.
...
NavigationView {
List {
NavigationLink(destination: Map()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
NavigationLink(destination: MarkerDemo()) {
Text("Marker Demo")
}
}
}
...
앱 미리보기 및 실행
미리보기를 새로고침하거나 앱을 실행하여 마커를 확인합니다.
돌출된 마커
altitude
및 altitudeMode
를 사용하여 마커를 지상 또는 3D 메시 위에 배치할 수 있습니다.
MarkerDemo.swift
의 mapMarker
선언을 extrudedMarker
라는 새 Marker
변수에 복사합니다.
altitude
에 0이 아닌 값을 설정합니다. 50이면 충분합니다.
altitudeMode
를 .relativeToMesh
로 변경하고 extruded
를 true
로 설정합니다. 여기의 코드 스니펫에서 latitude
및 longitude
를 사용하여 마커를 마천루 꼭대기에 배치합니다.
@State var extrudedMarker: Marker = .init(
position: .init(
latitude: 37.78980534,
longitude: -122.3969349,
altitude: 50.0),
altitudeMode: .relativeToMesh,
collisionBehavior: .required,
extruded: true,
drawsWhenOccluded: true,
sizePreserved: true,
zIndex: 0,
label: "Extruded"
)
앱을 다시 실행하거나 미리 봅니다. 마커가 3D 건물 위에 표시됩니다.
7. 지도에 모델을 추가합니다.
Model
는 Marker
와 동일한 방식으로 추가할 수 있습니다. URL로 액세스하거나 프로젝트에 로컬 파일로 추가할 수 있는 모델 파일이 필요합니다. 이 단계에서는 이 Codelab의 GitHub 저장소에서 다운로드할 수 있는 로컬 파일을 사용합니다.
프로젝트에 모델 파일 추가
Xcode 프로젝트에 Models
라는 새 폴더를 만듭니다.
GitHub 샘플 앱 저장소에서 모델을 다운로드합니다. Xcode 프로젝트 뷰에서 새 폴더로 드래그하여 프로젝트에 추가합니다.
타겟을 앱의 기본 타겟으로 설정해야 합니다.
프로젝트의 Build Phases(빌드 단계) > Copy Bundle Resources(번들 리소스 복사) 설정을 확인합니다. 모델 파일은 번들에 복사된 리소스 목록에 있어야 합니다. 표시되지 않으면 '+'를 클릭하여 추가합니다.
앱에 모델을 추가합니다.
ModelDemo.swift
라는 새 SwiftUI 파일을 만듭니다.
이전 단계와 같이 SwiftUI
및 GoogleMaps3D
에 import
문을 추가합니다.
body
의 VStack
내에 Map
를 선언합니다.
import SwiftUI
import GoogleMaps3D
struct ModelDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
번들에서 모델 경로를 가져옵니다. struct
외부에 이 코드를 추가합니다.
private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")
구조체 내에서 모델의 변수를 선언합니다.
fileUrl
가 제공되지 않은 경우 기본값을 제공합니다.
@State var balloonModel: Model = .init(
position: .init(
latitude: 37.791376,
longitude: -122.397571,
altitude: 300.0),
url: URL(fileURLWithPath: fileUrl?.relativePath ?? ""),
altitudeMode: .absolute,
scale: .init(x: 5, y: 5, z: 5),
orientation: .init(heading: 0, tilt: 0, roll: 0)
)
3. 지도에서 모델을 사용합니다.
Marker
를 추가할 때와 마찬가지로 Map
선언에 Model
참조를 제공하기만 하면 됩니다.
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
balloonModel
}
}
}
앱 미리보기 및 실행
대상이 ModelDemo()
이고 Text
'모델 데모'인 새 NavigationLink
를 GoogleMaps3DDemoApp.swift
에 추가합니다.
...
NavigationLink(destination: ModelDemo()) {
Text("Model Demo")
}
...
미리보기를 새로고침하거나 앱을 실행하여 모델을 확인합니다.
8. 지도에 선과 다각형을 그립니다.
이 단계에서는 3D 지도에 선과 다각형 도형을 추가하는 방법을 알아봅니다.
간단히 하기 위해 도형을 LatLngAltitude
객체의 배열로 정의합니다. 실제 애플리케이션에서는 파일, API 호출 또는 데이터베이스에서 데이터를 로드할 수 있습니다.
도형 데이터를 관리할 도형 객체를 만듭니다.
샌프란시스코 시내를 조망하는 새 Camera
정의를 MapHelpers.swift
에 추가합니다.
public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500)
프로젝트에 ShapesDemo.swift
라는 새 파일을 추가합니다. View
프로토콜을 구현하는 ShapesDemo
라는 struct
를 추가하고 여기에 body
를 추가합니다.
struct ShapesDemo: View {
@State var camera: Camera = .downtownSanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
도형 데이터를 관리하는 데 사용할 클래스는 Polyline
및 Polygon
입니다. ShapesDemo.swift
를 열고 다음과 같이 struct
에 추가합니다.
var polyline: Polyline = .init(coordinates: [
LatLngAltitude(latitude: 37.80515638571346, longitude: -122.4032569467164, altitude: 0),
LatLngAltitude(latitude: 37.80337073509504, longitude: -122.4012878349353, altitude: 0),
LatLngAltitude(latitude: 37.79925208843463, longitude: -122.3976697250461, altitude: 0),
LatLngAltitude(latitude: 37.7989102378512, longitude: -122.3983408725656, altitude: 0),
LatLngAltitude(latitude: 37.79887832784348, longitude: -122.3987094864192, altitude: 0),
LatLngAltitude(latitude: 37.79786443410338, longitude: -122.4066878788802, altitude: 0),
LatLngAltitude(latitude: 37.79549248916587, longitude: -122.4032992702785, altitude: 0),
LatLngAltitude(latitude: 37.78861484290265, longitude: -122.4019489189814, altitude: 0),
LatLngAltitude(latitude: 37.78618687561075, longitude: -122.398969592545, altitude: 0),
LatLngAltitude(latitude: 37.7892310309145, longitude: -122.3951458683092, altitude: 0),
LatLngAltitude(latitude: 37.7916358762409, longitude: -122.3981969390652, altitude: 0)
])
.stroke(GoogleMaps3D.Polyline.StrokeStyle(
strokeColor: UIColor(red: 0.09803921568627451, green: 0.403921568627451, blue: 0.8235294117647058, alpha: 1),
strokeWidth: 10.0,
outerColor: .white,
outerWidth: 0.2
))
.contour(GoogleMaps3D.Polyline.ContourStyle(isGeodesic: true))
var originPolygon: Polygon = .init(outerCoordinates: [
LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300),
LatLngAltitude(latitude: 37.7915324439261, longitude: -122.3982171091383, altitude: 300),
LatLngAltitude(latitude: 37.79166617650914, longitude: -122.3980478493319, altitude: 300),
LatLngAltitude(latitude: 37.79178986470217, longitude: -122.3982041104199, altitude: 300),
LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300 )
],
altitudeMode: .relativeToGround)
.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.green, extruded: true) )
var destinationPolygon: Polygon = .init(outerCoordinates: [
LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300),
LatLngAltitude(latitude: 37.80503794515428, longitude: -122.4032633416024, altitude: 300),
LatLngAltitude(latitude: 37.80517850164195, longitude: -122.4031056058006, altitude: 300),
LatLngAltitude(latitude: 37.80529346901115, longitude: -122.4032622466595, altitude: 300),
LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300 )
],
altitudeMode: .relativeToGround)
.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.red, extruded: true) )
사용된 초기화 매개변수를 확인합니다.
altitudeMode: .relativeToGround
는 다각형을 지상에서 특정 높이로 돌출하는 데 사용됩니다.altitudeMode: .clampToGround
는 다중선이 지구 표면의 모양을 따르도록 하는 데 사용됩니다..init()
가 호출된 후styleOptions()
에 메서드 호출을 체이닝하여Polygon
객체에 스타일이 설정됩니다.
지도에 도형 추가
이전 단계와 마찬가지로 도형을 Map
폐쇄에 직접 추가할 수 있습니다. VStack
내에 Map
를 만듭니다.
...
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
polyline
originPolygon
destinationPolygon
}
}
}
...
앱 미리보기 및 실행
미리보기 코드를 추가하고 Xcode의 미리보기 창에서 앱을 검사합니다.
#Preview {
ShapesDemo()
}
앱을 실행하려면 새 데모 뷰를 여는 새 NavigationLink
를 GoogleMaps3DDemoApp.swift
에 추가합니다.
...
NavigationLink(destination: ShapesDemo()) {
Text("Shapes Demo")
}
...
앱을 실행하고 추가한 도형을 살펴봅니다.
9. 장소 마커의 탭 이벤트 처리
이 단계에서는 사용자가 장소 마커를 탭할 때 응답하는 방법을 알아봅니다.
참고: 지도에 장소 마커를 표시하려면 MapMode
를 .hybrid
로 설정해야 합니다.
탭을 처리하려면 Map.onPlaceTap
메서드를 구현해야 합니다.
onPlaceTap
이벤트는 탭한 장소 마커의 장소 ID를 가져올 수 있는 PlaceTapInfo
객체를 제공합니다.
장소 ID를 사용하여 Places SDK 또는 Places API를 통해 추가 세부정보를 조회할 수 있습니다.
새 Swift 뷰 추가
다음 코드를 PlaceTapDemo.swift
라는 새 Swift 파일에 추가합니다.
import GoogleMaps3D
import SwiftUI
struct PlaceTapDemo: View {
@State var camera: Camera = .sanFrancisco
@State var isPresented = false
@State var tapInfo: PlaceTapInfo?
var body: some View {
Map(camera: $camera, mode: .hybrid)
.onPlaceTap { tapInfo in
self.tapInfo = tapInfo
isPresented.toggle()
}
.alert(
"Place tapped - \(tapInfo?.placeId ?? "nil")",
isPresented: $isPresented,
actions: { Button("OK") {} }
)
}
}
#Preview {
PlaceTapDemo()
}
앱 미리보기 및 실행
미리보기 창을 열어 앱을 미리 봅니다.
앱을 실행하려면 GoogleMaps3DDemoApp.swift
에 새 NavigationLink
를 추가합니다.
...
NavigationLink(destination: PlaceTapDemo()) {
Text("Place Tap Demo")
}
...
10. (선택사항) 한 단계 더 나아가기
고급 카메라 애니메이션
비행 시뮬레이터나 하이킹 또는 달리기 재생과 같이 위치 또는 카메라 상태의 시퀀스 또는 목록을 따라 원활하게 애니메이션을 적용해야 하는 사용 사례가 있습니다.
이 단계에서는 파일에서 위치 목록을 로드하고 각 위치를 순차적으로 애니메이션 처리하는 방법을 알아봅니다.
위치 시퀀스가 포함된 파일을 로드합니다.
GitHub 샘플 앱 저장소에서 flightpath.json
를 다운로드합니다.
Xcode 프로젝트에 JSON
라는 새 폴더를 만듭니다.
Xcode에서 flightpath.json
를 JSON
폴더로 드래그합니다.
타겟을 앱의 기본 타겟으로 설정합니다. 프로젝트의 번들 리소스 복사 설정에 이 파일이 포함되어 있는지 확인합니다.
앱에 FlightPathData.swift
및 FlightDataLoader.swift
라는 새 Swift 파일 2개를 만듭니다.
다음 코드를 앱에 복사합니다. 이 코드는 'flighpath.json'이라는 로컬 파일을 읽고 JSON으로 디코딩하는 구조체와 클래스를 만듭니다.
FlightPathData
및 FlightPathLocation
구조체는 JSON 파일의 데이터 구조를 Swift 객체로 나타냅니다.
FlightDataLoader
클래스는 파일에서 데이터를 읽고 디코딩합니다. ObservableObject
프로토콜을 채택하여 앱이 데이터 변경사항을 관찰할 수 있도록 합니다.
파싱된 데이터는 게시된 속성을 통해 노출됩니다.
FlightPaths.swift
import GoogleMaps3D
struct FlightPathData: Decodable {
let flight: [FlightPathLocation]
}
struct FlightPathLocation: Decodable {
let timestamp: Int64
let latitude: Double
let longitude: Double
let altitude: Double
let bearing: Double
let speed: Double
}
FlightDataLoader.swift
import Foundation
public class FlightDataLoader : ObservableObject {
@Published var flightPathData: FlightPathData = FlightPathData(flight:[])
@Published var isLoaded: Bool = false
public init() {
load("flightpath.json")
}
public func load(_ path: String) {
if let url = Bundle.main.url(forResource: path, withExtension: nil){
if let data = try? Data(contentsOf: url){
let jsondecoder = JSONDecoder()
do{
let result = try jsondecoder.decode(FlightPathData.self, from: data)
flightPathData = result
isLoaded = true
}
catch {
print("Error trying to load or parse the JSON file.")
}
}
}
}
}
각 위치를 따라 카메라 애니메이션 처리
일련의 단계 간에 카메라를 애니메이션 처리하려면 KeyframeAnimator
를 사용합니다.
각 Keyframe
는 CubicKeyframe
로 생성되므로 카메라 상태의 변경사항이 원활하게 애니메이션됩니다.
flyCameraTo()
를 사용하면 뷰가 각 위치 간에 '튀게' 됩니다.
먼저 MapHelpers.swift
에서 'innsbruck'이라는 카메라를 선언합니다.
public static var innsbruck: Camera = .init(
latitude: 47.263,
longitude: 11.3704,
altitude: 640.08,
heading: 237,
tilt: 80.0,
roll: 0.0,
range: 200)
이제 FlyAlongRoute.swift
라는 새 파일에서 새 뷰를 설정합니다.
SwiftUI
및 GoogleMaps3D
를 가져옵니다. VStack
내에 Map
및 Button
를 추가합니다. 불리언 animation
변수의 상태를 전환하도록 Button
를 설정합니다.
초기화 시 JSON 파일을 로드하는 FlightDataLoader
의 State
객체를 선언합니다.
import GoogleMaps3D
import SwiftUI
struct FlyAlongRoute: View {
@State private var camera: Camera = .innsbruck
@State private var flyToDuration: TimeInterval = 5
@State var animation: Bool = true
@StateObject var flightData: FlightDataLoader = FlightDataLoader()
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
Button("Fly Along Route"){
animation.toggle()
}
}
}
}
키프레임 만들기
기본 프로세스는 애니메이션 시퀀스의 새 프레임을 반환하는 함수를 만드는 것입니다. 각 새 프레임은 애니메이터가 애니메이션할 다음 카메라 상태를 정의합니다. 이 함수가 생성되면 파일의 각 위치를 차례로 사용하여 함수를 호출합니다.
FlyAlongRoute
구조체에 두 개의 함수를 추가합니다. makeKeyFrame
함수는 카메라 상태가 포함된 CubicKeyframe
를 반환합니다. makeCamera
함수는 비행 데이터 시퀀스의 단계를 가져와 해당 단계를 나타내는 Camera
객체를 반환합니다.
func makeKeyFrame(step: FlightPathLocation) -> CubicKeyframe<Camera> {
return CubicKeyframe(
makeCamera(step: step),
duration: flyToDuration
)
}
func makeCamera(step: FlightPathLocation) -> Camera {
return .init(
latitude: step.latitude,
longitude: step.longitude,
altitude: step.altitude,
heading: step.bearing,
tilt: 75,
roll: 0,
range: 200
)
}
애니메이션 조합
Map
초기화 후 keyframeAnimator
를 호출하고 초깃값을 설정합니다.
비행 경로의 첫 번째 위치를 기반으로 하는 초기 카메라 상태가 필요합니다.
애니메이션은 상태를 변경하는 변수를 기반으로 트리거되어야 합니다.
keyframeAnimator
콘텐츠는 지도여야 합니다.
실제 키프레임 목록은 비행 경로의 각 위치를 반복하여 생성됩니다.
VStack {
Map(camera: $camera, mode: .hybrid)
.keyframeAnimator(
initialValue: makeCamera(step: flightData.flightPathData.flight[0]),
trigger: animation,
content: { view, value in
Map(camera: .constant(value), mode: .hybrid)
},
keyframes: { _ in
KeyframeTrack(content: {
for i in 1...flightData.flightPathData.flight.count-1 {
makeKeyFrame(step: flightData.flightPathData.flight[i])
}
})
}
)
}
앱을 미리 보고 실행합니다.
미리보기 창을 열어 뷰를 미리 봅니다.
대상이 FlightPathDemo()
인 새 NavigationLink
를 GoogleMaps3DDemoApp.swift
에 추가하고 앱을 실행하여 사용해 봅니다.
11. 축하합니다
다음과 같은 애플리케이션을 빌드했습니다.
- 앱에 기본 3D 지도를 추가합니다.
- 지도에 마커, 선, 다각형, 모델을 추가합니다.
- 카메라를 제어하여 지도와 특정 위치를 둘러보도록 하는 코드를 구현합니다.
학습한 내용
- Xcode SwiftUI 앱에
GoogleMaps3D
패키지를 추가하는 방법 - API 키와 기본 뷰를 사용하여 3D 지도를 초기화하는 방법
- 지도에 마커, 3D 모델, 선, 다각형을 추가하는 방법
- 카메라를 제어하여 다른 위치로 이동하는 애니메이션을 만드는 방법
- 장소 마커의 클릭 이벤트를 처리하는 방법
다음 단계
- iOS용 지도 3D SDK로 할 수 있는 작업에 관한 자세한 내용은 개발자 가이드를 참고하세요.
- 다음 설문조사에 답하여 Google에서 가장 유용한 콘텐츠를 만들 수 있도록 도와주세요.