1. Antes de começar
Este codelab ensina a criar um app de mapas 3D no SwiftUI usando o SDK do Maps 3D para iOS.
Você vai aprender a:
- Como controlar a câmera para visualizar locais e voar pelo mapa.
- Como adicionar marcadores e modelos
- Como desenhar linhas e polígonos
- Como processar cliques do usuário em marcadores de lugar.
Pré-requisitos
- Um projeto do Google Console com faturamento ativado
- Uma chave de API, opcionalmente restrita ao SDK do Maps 3D para iOS.
- Conhecimento básico de desenvolvimento para iOS com SwiftUI.
Atividades deste laboratório
- Configurar o Xcode e importar o SDK usando o Swift Package Manager
- Configurar o app para usar uma chave de API
- Adicionar um mapa 3D básico ao seu app
- Controlar a câmera para voar para e em torno de locais específicos
- Adicionar marcadores, linhas, polígonos e modelos ao mapa
O que é necessário
- Xcode 15 ou mais recente.
2. Começar a configuração
Para a etapa de ativação a seguir, é necessário ativar o SDK 3D do Maps para iOS.
Configurar a Plataforma Google Maps
Caso você ainda não tenha uma conta do Google Cloud Platform e um projeto com faturamento ativado, veja como criá-los no guia da Plataforma Google Maps.
- No Console do Cloud, clique no menu suspenso do projeto e selecione o projeto que você quer usar neste codelab.
- Ative as APIs e os SDKs da Plataforma Google Maps necessários para este codelab no Google Cloud Marketplace. Para fazer isso, siga as etapas descritas neste vídeo ou nesta documentação.
- Gere uma chave de API na página Credenciais do Console do Cloud. Siga as etapas indicadas neste vídeo ou nesta documentação. Todas as solicitações à Plataforma Google Maps exigem uma chave de API.
Ativar o SDK do Maps 3D para iOS
Você pode encontrar o SDK 3D do Maps para iOS usando o link do menu "Plataforma Google Maps > APIs e serviços" no console.
Clique em "Ativar" para ativar a API no projeto selecionado.
3. Criar um app SwiftUI básico
Observação: você pode encontrar o código da solução para cada etapa no repositório de apps de exemplo do codelab no GitHub .
Crie um novo app no Xcode.
O código dessa etapa pode ser encontrado na pasta GoogleMaps3DDemo no GitHub.
Abra o Xcode e crie um novo app. Especifique a SwiftUI.
Chame o app de GoogleMaps3DDemo
, com um nome de pacote com.example.GoogleMaps3DDemo
.
Importar a biblioteca GoogleMaps3D para o projeto
Adicione o SDK ao seu projeto usando o Swift Package Manager.
No projeto ou espaço de trabalho do Xcode, acesse "File > Add Package Dependencies". Insira https://github.com/googlemaps/ios-maps-3d-sdk como o URL, pressione Enter para extrair o pacote e clique em "Adicionar pacote".
Na janela "Escolher produtos do pacote", verifique se GoogleMaps3D
será adicionado ao destino principal designado. Quando terminar, clique em "Adicionar pacote".
Para verificar a instalação, acesse o painel "Geral" do destino. Em "Frameworks, bibliotecas e conteúdo incorporado", você vai encontrar os pacotes instalados. Você também pode conferir a seção "Dependências do pacote" do Project Navigator para verificar o pacote e a versão dele.
Adicionar sua chave de API
Você pode codificar a chave da API no app, mas essa não é uma prática recomendada. A adição de um arquivo de configuração permite manter a chave de API como um segredo e evita que ela seja verificada no controle de origem.
Criar um novo arquivo de configuração na pasta raiz do projeto
No Xcode, verifique se você está visualizando a janela do explorador de projetos. Clique com o botão direito do mouse na raiz do projeto e selecione "New File from Template". Role a tela até encontrar "Arquivo de configurações de configuração". Selecione essa opção e clique em "Próxima". Nomeie o arquivo como Config.xcconfig
e verifique se a pasta raiz do projeto está selecionada. Clique em "Criar" para criar o arquivo.
No editor, adicione uma linha ao arquivo de configuração da seguinte maneira: MAPS_API_KEY = YOUR_API_KEY
Substitua YOUR_API_KEY
pela sua chave de API.
Adicione essa configuração a Info.plist
.
Para fazer isso, selecione a raiz do projeto e clique na guia "Informações".
Adicione uma nova propriedade chamada MAPS_API_KEY
com o valor $(MAPS_API_KEY)
.
O código de app de exemplo tem um arquivo Info.plist
que especifica essa propriedade.
Adicionar um mapa
Abra o arquivo chamado GoogleMaps3DDemoApp.swift
. Esse é o ponto de entrada e a navegação principal do app.
Ele chama ContentView()
, que mostra uma mensagem Hello World.
Abra ContentView.swift
no editor.
Adicione uma instrução import
para GoogleMaps3D
.
Exclua o código dentro do bloco var body: some View {}
. Declare uma nova Map()
dentro do body
.
A configuração mínima necessária para inicializar um Map
é um MapMode
. Ele tem dois valores possíveis:
.hybrid
: imagens de satélite com vias e marcadores ou.satellite
: somente imagens de satélite.
Escolha a .hybrid
.
Seu arquivo ContentView.swift
vai ficar assim.
import GoogleMaps3D
import SwiftUI
@main
struct ContentView: View {
var body: some View {
Map(mode: .hybrid)
}
}
Defina sua chave de API.
A chave da API precisa ser definida antes da inicialização do mapa.
Para fazer isso, defina Map.apiKey
no manipulador de eventos init()
de qualquer View
que contenha um mapa. Também é possível definir o valor em GoogleMaps3DDemoApp.swift
antes de chamar ContentView()
.
Em GoogleMaps3DDemoApp.swift
, defina Map.apiKey
no manipulador de eventos onAppear
do WindowGroup
.
Buscar a chave de API no arquivo de configuração
Use Bundle.main.infoDictionary
para acessar a configuração MAPS_API_KEY
que você criou no arquivo de configuração.
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
}
}
}
Crie e execute o app para verificar se ele carrega corretamente. Você vai ver um mapa do globo.
4. Usar uma câmera para controlar a visualização do mapa
Criar um objeto de estado da câmera
As visualizações de mapa 3D são controladas pela classe Camera
. Nesta etapa, você vai aprender a especificar o local, a altitude, a direção, a inclinação, a rotação e o alcance para personalizar a visualização do mapa.
Criar uma classe Helpers para armazenar as configurações da câmera
Adicione um novo arquivo vazio chamado MapHelpers.swift
. No novo arquivo, importe GoogleMaps3D
e adicione uma extensão à classe Camera
. Adicione uma variável chamada sanFrancisco
. Inicialize essa variável como um novo objeto Camera
. Localize a câmera em latitude: 37.39, longitude: -122.08
.
import GoogleMaps3D
extension Camera {
public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}
Adicionar uma nova visualização ao app
Crie um novo arquivo chamado CameraDemo.swift
. Adicione o esboço básico de uma nova visualização do SwiftUI ao arquivo.
Adicione uma variável @State
com o nome camera
do tipo Camera
. Inicialize-a para a câmera sanFrancisco
que você acabou de definir.
O uso de @State
permite vincular o mapa ao estado da câmera e usá-lo como a fonte da verdade.
@State var camera: Camera = .sanFrancisco
Mude a chamada de função Map()
para incluir uma propriedade camera
. Use a vinculação de estado da câmera $camera
para inicializar a propriedade camera
no objeto @State
da câmera (.sanFrancisco
).
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
Adicionar a interface de navegação básica ao app
Adicione um NavigationView
ao ponto de entrada principal do app, GoogleMaps3DDemoApp.swift
.
Isso permite que os usuários vejam uma lista de demonstrações e cliquem em cada uma para abri-las.
Edite GoogleMaps3DDemoApp.swift
para adicionar uma nova NavigationView
.
Adicione um List
que contenha duas declarações NavigationLink
.
O primeiro NavigationLink
precisa abrir ContentView()
com uma descrição Text
Basic Map
.
O segundo NavigationLink
vai abrir CameraDemo()
.
...
NavigationView {
List {
NavigationLink(destination: ContentView()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
}
}
...
Adicionar uma visualização do Xcode
As prévias são um recurso poderoso do Xcode que permite visualizar e interagir com o app enquanto você faz mudanças nele.
Para adicionar uma visualização, abra CameraDemo.swift
. Adicione um bloco de código #Preview {}
fora do struct
.
#Preview {
CameraDemo()
}
Abra ou atualize o painel de visualização no Xcode. O mapa deve mostrar São Francisco.
Configurar visualizações 3D personalizadas
É possível especificar outros parâmetros para controlar a câmera:
heading
: a direção em graus a partir do norte para apontar a câmera.tilt
: o ângulo de inclinação em graus, em que 0 é diretamente acima e 90 é horizontal.roll
: o ângulo de inclinação em torno do plano vertical da câmera, em grausrange
: a distância em metros da câmera em relação à latitude, longitudealtitude
: a altura da câmera acima do nível do mar
Se você não fornecer nenhum desses parâmetros adicionais, os valores padrão serão usados.
Para que a visualização da câmera mostre mais dados em 3D, defina os parâmetros iniciais para mostrar uma visualização mais próxima e inclinada.
Edite o Camera
definido em MapHelpers.swift
para incluir valores para altitude
, heading
, tilt
, roll
e range
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)
Crie e execute o app para conferir e testar a nova visualização em 3D.
5. Animações básicas da câmera
Até agora, você usou a câmera para especificar um único local com inclinação, altitude, rumo e alcance. Nesta etapa, você vai aprender a mover a visualização da câmera animando essas propriedades de um estado inicial para um novo.
Voar para um local
Você vai usar o método Map.flyCameraTo()
para animar a câmera do local inicial para um novo local.
O método flyCameraTo()
usa vários parâmetros:
- um
Camera
que representa o local final. duration
: quanto tempo a animação vai durar, em segundos.trigger
: um objeto observável que aciona a animação quando o estado dele muda.completion
: código que será executado quando a animação for concluída.
Definir um local para voar
Abra o arquivo MapHelpers.swift
.
Defina um novo objeto de câmera para mostrar Seattle.
public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)
Adicione um botão para acionar a animação.
Abra o CameraDemo.swift
. Declare uma nova variável booleana dentro do struct
.
Chame-a de animate
com um valor inicial de false
.
@State private var animate: Bool = false
Adicione um Button
abaixo do VStack
. O Button
vai iniciar a animação do mapa.
Dê ao Button
um Text
adequado, como "Start Flying".
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") {
}
}
}
}
Na declaração de fechamento do botão, adicione um código para alternar o estado da variável animate
.
Button("Start Flying") {
animate.toggle()
}
Inicia a animação.
Adicione o código para acionar a animação flyCameraTo()
quando o estado da variável animate
mudar.
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
Voar em torno de um local
É possível voar em torno de um local usando o método Map.flyCameraAround()
. Esse método usa vários parâmetros:
- um
Camera
que define o local e a visualização. - uma
duration
em segundos. rounds
: o número de vezes que a animação será repetida.trigger
: um objeto observável que aciona a animação.callback
: código que será executado quando a animação for executada.
Defina uma nova variável @State
chamada flyAround
, com um valor inicial de false
.
Depois disso, adicione uma chamada para flyCameraAround()
imediatamente após a chamada do método flyCameraTo()
.
A duração do voo precisa ser relativamente longa para que a visualização mude sem problemas.
Ative a animação flyCameraAround()
mudando o estado do objeto de acionamento quando flyCameraTo()
for concluído.
O código ficará assim.
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()
}
Visualize ou execute o app para conferir se a câmera voa ao redor do destino quando a animação flyCameraTo()
é concluída.
6. Adicione um marcador ao mapa.
Nesta etapa, você vai aprender a desenhar um marcador no mapa.
Você vai criar um objeto Marker
e adicioná-lo ao mapa. O SDK vai usar um ícone padrão para o marcador. Por fim, você vai ajustar a altitude do marcador e outras propriedades para mudar a forma como ele aparece.
Crie uma nova visualização SwiftUI para a demonstração do marcador.
Adicione um novo arquivo Swift ao projeto. Dê o nome MarkerDemo.swift
a ele.
Adicione o contorno de uma visualização do SwiftUI e inicialize o mapa como fez na CameraDemo
.
import SwiftUI
import GoogleMaps3D
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
Inicializar um objeto Marker
Declare uma nova variável de marcador com o nome mapMarker
. Na parte de cima do bloco de código struct
em MarkerDemo.swift
.
Coloque a definição na linha abaixo da declaração camera
. Este código de exemplo inicializa todas as propriedades disponíveis.
@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"
)
Adicione o marcador ao mapa.
Para desenhar o marcador, adicione-o a uma interdição chamada quando o mapa for criado.
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
mapMarker
}
}
}
}
Adicione um novo NavigationLink
a GoogleMaps3DDemoApp.swift
com um destino de MarkerDemo()
e Text
descrevendo-o como "Marker Demo".
...
NavigationView {
List {
NavigationLink(destination: Map()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
NavigationLink(destination: MarkerDemo()) {
Text("Marker Demo")
}
}
}
...
Visualizar e executar o app
Atualize a visualização ou execute o app para conferir o marcador.
Marcadores extrudados
Os marcadores podem ser colocados acima do solo ou da malha 3D usando altitude
e altitudeMode
.
Copie a declaração mapMarker
em MarkerDemo.swift
para uma nova variável Marker
chamada extrudedMarker
.
Defina um valor diferente de zero para altitude
. 50 é suficiente.
Mude altitudeMode
para .relativeToMesh
e defina extruded
como true
. Use latitude
e longitude
do snippet de código aqui para colocar o marcador em cima de um arranha-céu.
@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"
)
Execute ou visualize o app novamente. O marcador vai aparecer em cima de um edifício 3D.
7. Adicione um modelo ao mapa.
Um Model
pode ser adicionado da mesma forma que um Marker
. Você vai precisar de um arquivo de modelo que possa ser acessado por URL ou adicionado como um arquivo local no projeto. Nesta etapa, vamos usar um arquivo local que você pode fazer o download no repositório do GitHub para este codelab.
Adicionar um arquivo de modelo ao projeto
Crie uma nova pasta no projeto Xcode chamada Models
.
Faça o download do modelo no repositório de apps de exemplo do GitHub. Adicione-o ao seu projeto arrastando-o para a nova pasta na visualização do projeto do Xcode.
Defina o destino como o destino principal do app.
Verifique as configurações de Build Phases > Copy Bundle Resources do seu projeto. O arquivo de modelo precisa estar na lista de recursos copiados para o pacote. Se não estiver, clique em "+" para adicionar.
Adicione o modelo ao app.
Crie um novo arquivo SwiftUI chamado ModelDemo.swift
.
Adicione instruções import
para SwiftUI
e GoogleMaps3D
, como nas etapas anteriores.
Declare uma Map
dentro de uma VStack
na body
.
import SwiftUI
import GoogleMaps3D
struct ModelDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
Receba o caminho do modelo do seu pacote. Adicione o código para isso fora da struct
.
private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")
Declare uma variável para o modelo dentro da estrutura.
Forneça um valor padrão caso fileUrl
não seja fornecido.
@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. Use o modelo com seu mapa.
Assim como ao adicionar um Marker
, basta fornecer a referência ao Model
na declaração Map
.
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
balloonModel
}
}
}
Visualizar e executar o app
Adicione um novo NavigationLink
a GoogleMaps3DDemoApp.swift
, com um destino de ModelDemo()
e o Text
"Model Demo".
...
NavigationLink(destination: ModelDemo()) {
Text("Model Demo")
}
...
Atualize a visualização ou execute o app para conferir o modelo.
8. Desenhe uma linha e um polígono no mapa.
Nesta etapa, você vai aprender a adicionar linhas e formas poligonais ao mapa 3D.
Para simplificar, você vai definir as formas como matrizes de objetos LatLngAltitude
. Em um aplicativo real, os dados podem ser carregados de um arquivo, de uma chamada de API ou de um banco de dados.
Crie alguns objetos de forma para gerenciar os dados de forma.
Adicione uma nova definição de Camera
a MapHelpers.swift
que analise o centro de São Francisco.
public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500)
Adicione um novo arquivo ao projeto chamado ShapesDemo.swift
. Adicione uma struct
chamada ShapesDemo
que implemente o protocolo View
e adicione uma body
a ela.
struct ShapesDemo: View {
@State var camera: Camera = .downtownSanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
As classes que você vai usar para gerenciar dados de forma são Polyline
e Polygon
. Abra ShapesDemo.swift
e adicione-os ao struct
da seguinte maneira:
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) )
Observe os parâmetros de inicialização usados.
altitudeMode: .relativeToGround
é usado para extrair os polígonos a uma altura específica acima do solo.altitudeMode: .clampToGround
é usado para fazer a polilinha seguir a forma da superfície da Terra.- Os estilos são definidos nos objetos
Polygon
encadeando uma chamada de método parastyleOptions()
depois que.init()
é chamado
Adicionar as formas ao mapa
Assim como nas etapas anteriores, as formas podem ser adicionadas diretamente à interseção Map
. Crie o Map
dentro de um VStack
.
...
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
polyline
originPolygon
destinationPolygon
}
}
}
...
Visualizar e executar o app
Adicione o código de visualização e inspecione o app no painel de visualização do Xcode.
#Preview {
ShapesDemo()
}
Para executar o app, adicione um novo NavigationLink
a GoogleMaps3DDemoApp.swift
que abra a nova visualização de demonstração.
...
NavigationLink(destination: ShapesDemo()) {
Text("Shapes Demo")
}
...
Execute o app e confira as formas que você adicionou.
9. Processar eventos de toque em marcadores de lugar
Nesta etapa, você vai aprender a responder aos toques do usuário nos marcadores de lugar.
Observação: para ver os marcadores de lugar no mapa, defina MapMode
como .hybrid
.
O processamento do toque requer a implementação do método Map.onPlaceTap
.
O evento onPlaceTap
fornece um objeto PlaceTapInfo
, em que é possível acessar o ID do lugar do marcador de lugar tocado.
É possível usar o ID do lugar para procurar mais detalhes usando o SDK do Places ou a API Places.
Adicionar uma nova visualização Swift
Adicione o código abaixo a um novo arquivo Swift chamado PlaceTapDemo.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()
}
Visualizar e executar o app
Abra o painel de visualização para conferir o app.
Para executar o app, adicione uma nova NavigationLink
a GoogleMaps3DDemoApp.swift
.
...
NavigationLink(destination: PlaceTapDemo()) {
Text("Place Tap Demo")
}
...
10. (Opcional) Vá além
Animações avançadas da câmera
Alguns casos de uso exigem animação suave em uma sequência ou lista de locais ou estados da câmera, por exemplo, um simulador de voo ou a reprodução de uma caminhada ou corrida.
Nesta etapa, você vai aprender a carregar uma lista de locais de um arquivo e animar cada local em sequência.
Carregar um arquivo com uma sequência de locais.
Faça o download de flightpath.json
no repositório de apps de exemplo do GitHub.
Crie uma nova pasta no projeto Xcode chamada JSON
.
Arraste flightpath.json
para a pasta JSON
no Xcode.
Defina o destino como o destino principal do app. Verifique se as configurações de recursos de cópia de pacote do projeto incluem esse arquivo.
Crie dois novos arquivos Swift no app chamados FlightPathData.swift
e FlightDataLoader.swift
.
Copie o código abaixo no seu app. Esse código cria estruturas e classes que leem um arquivo local chamado "flighpath.json" e o decodifica como JSON.
Os structs FlightPathData
e FlightPathLocation
representam a estrutura de dados no arquivo JSON como objetos Swift.
A classe FlightDataLoader
lê e decodifica os dados do arquivo. Ele adota o protocolo ObservableObject
para permitir que o app observe mudanças nos dados.
Os dados analisados são expostos por uma propriedade publicada.
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.")
}
}
}
}
}
Animar a câmera em cada local
Para animar a câmera entre uma sequência de etapas, use uma KeyframeAnimator
.
Cada Keyframe
será criado como um CubicKeyframe
, para que as mudanças no estado da câmera sejam animadas sem problemas.
O uso de flyCameraTo()
faz com que a visualização "rebote" entre cada local.
Primeiro, declare uma câmera chamada "innsbruck" em MapHelpers.swift
.
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)
Agora, configure uma nova visualização em um arquivo chamado FlyAlongRoute.swift
.
Importe SwiftUI
e GoogleMaps3D
. Adicione uma Map
e uma Button
dentro de uma VStack
. Configure o Button
para alternar o estado da variável booleana animation
.
Declare um objeto State
para FlightDataLoader
, que vai carregar o arquivo JSON quando ele for inicializado.
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()
}
}
}
}
Criar os frames-chave
O processo básico é criar uma função que retorne um novo frame na sequência de animação. Cada novo frame define o próximo estado da câmera para a animação. Depois que essa função for criada, chame-a com cada local do arquivo em sequência.
Adicione duas funções à estrutura FlyAlongRoute
. A função makeKeyFrame
retorna um CubicKeyframe
com um estado da câmera. A função makeCamera
considera uma etapa na sequência de dados de voo e retorna um objeto Camera
que representa essa etapa.
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
)
}
Criar a animação
Chame o keyframeAnimator
após a inicialização do Map
e defina os valores iniciais.
Você vai precisar de um estado inicial da câmera com base no primeiro local no trajeto do voo.
A animação precisa ser acionada com base em uma variável que muda de estado.
O conteúdo de keyframeAnimator
precisa ser um mapa.
A lista real de keyframes é gerada com a repetição de cada local no caminho do voo.
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])
}
})
}
)
}
Visualize e execute o app.
Abra o painel de visualização para conferir sua visualização.
Adicione um novo NavigationLink
com um destino de FlightPathDemo()
para GoogleMaps3DDemoApp.swift
e execute o app para testar.
11. Parabéns
Você criou um aplicativo que
- Adiciona um mapa 3D básico ao seu app.
- Adiciona marcadores, linhas, polígonos e modelos ao mapa.
- Implementa o código para controlar a câmera e fazer com que ela voe pelo mapa e em torno de locais específicos.
O que você aprendeu
- Como adicionar o pacote
GoogleMaps3D
a um app SwiftUI do Xcode. - Como inicializar um mapa 3D com uma chave de API e uma visualização padrão.
- Como adicionar marcadores, modelos 3D, linhas e polígonos ao mapa.
- Como controlar a câmera para animar o movimento para outro local.
- Como processar eventos de clique em marcadores de lugar.
A seguir
- Confira o guia para desenvolvedores e saiba mais sobre o que você pode fazer com o SDK do Maps 3D para iOS.
- Ajude a criar conteúdo útil respondendo à seguinte pesquisa: