1. 시작하기 전에
이 Codelab에서는 SwiftUI와 함께 iOS용 Maps SDK를 사용하는 방법을 알아봅니다.

사전 준비 사항
- Swift 기본 지식
- SwiftUI 기본 지식
수행할 작업
- SwiftUI를 사용하여 iOS용 Maps SDK를 사용하고 iOS 앱에서 Google 지도를 iOS 앱에 추가합니다.
- 지도에 마커 추가하기.
- SwiftUI 뷰에서 GMSMapView객체로 상태를 전달합니다.
필요한 사항
- Xcode 11.0 이상
- 결제가 사용 설정된 Google 계정
- iOS용 Maps SDK
- Carthage
2. 설정하기
다음 사용 설정 단계를 진행하려면 iOS용 Maps SDK를 사용 설정합니다.
Google Maps Platform 설정
Google Cloud Platform 계정 및 결제가 사용 설정된 프로젝트가 없는 경우 Google Maps Platform 시작하기 가이드를 참고하여 결제 계정 및 프로젝트를 만듭니다.
- Cloud Console에서 프로젝트 드롭다운 메뉴를 클릭하고 이 Codelab에 사용할 프로젝트를 선택합니다.

- Google Cloud Marketplace에서 이 Codelab에 필요한 Google Maps Platform API 및 SDK를 사용 설정합니다. 이렇게 하려면 이 동영상 또는 이 문서의 단계를 따릅니다.
- Cloud Console의 Credentials 페이지에서 API 키를 생성합니다. 이 동영상 또는 이 문서의 단계를 따릅니다. Google Maps Platform으로 전송되는 모든 요청에는 API 키가 필요합니다.
3. 시작 코드 다운로드하기
빠르게 시작할 수 있도록 이 Codelab을 따라하는 데 도움이 되는 시작 코드가 있습니다. 해법으로 바로 넘어갈 수 있지만 모든 단계를 따라하면서 직접 빌드하려면 계속 읽으시기 바랍니다.
- git을 설치한 경우 저장소를 클론합니다.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git
또는 다음 버튼을 클릭하여 소스 코드를 다운로드할 수도 있습니다.
- 코드를 받으면 터미널 cd안에서starter/GoogleMapsSwiftUI디렉터리로 이동합니다.
- carthage update --platform iOS를 실행하여 iOS용 Maps SDK를 다운로드하세요.
- 마지막으로 Xcode에서 GoogleMapsSwiftUI.xcodeproj파일을 엽니다.
4. 코드 개요
다운로드한 시작 프로젝트에서는 다음 클래스가 제공되고 구현되어 있습니다.
- AppDelegate- 애플리케이션의- UIApplicationDelegate입니다. 여기에서 iOS용 Maps SDK가 초기화됩니다.
- City- 도시를 나타내는 구조체 (도시의 이름과 좌표 포함)
- MapViewController- Google 지도 (GMSMapView가 포함된 간단한 UIKit- UIViewController)
- SceneDelegate-- ContentView가 인스턴스화되는 애플리케이션의- UIWindowSceneDelegate입니다.
또한 다음 클래스에는 부분 구현이 있으며 이 Codelab을 마칠 때까지 직접 완료합니다.
- ContentView- 앱이 포함된 최상위 SwiftUI 뷰입니다.
- MapViewControllerBridge- UIKit 뷰를 SwiftUI 뷰에 연결하는 클래스입니다. 특히 SwiftUI에서- MapViewController에 액세스할 수 있게 하는 클래스입니다.
5. SwiftUI 사용과 UIKit 비교
SwiftUI는 iOS 애플리케이션 개발을 위한 UIKit의 대체 UI 프레임워크로 iOS 13에서 도입되었습니다. SwiftUI는 이전 UIKit과 비교할 때 여러 가지 장점이 있습니다. 다음과 같습니다...
- 상태가 변경되면 뷰가 자동으로 업데이트됩니다. 상태라는 객체를 사용하면 포함된 기본 값을 변경하면 UI가 자동으로 업데이트됩니다.
- 실시간 미리보기를 사용하면 개발 속도를 높일 수 있습니다. 실시간 미리보기는 Xcode를 미리 볼 수 있으므로 SwiftUI 뷰의 미리보기를 쉽게 볼 수 있도록 코드를 빌드하고 에뮬레이터에 배포할 필요성을 최소화합니다.
- 정보 소스는 Swift에 있습니다. SwiftUI의 모든 뷰는 Swift에서 선언되므로 인터페이스 빌더를 더 이상 사용할 필요가 없습니다.
- UIKit와 상호 운용됩니다. UIKit와의 상호 운용성은 기존 앱이 SwiftUI를 기존 뷰와 점진적으로 사용할 수 있게 해줍니다. 또한 iOS용 Maps SDK와 같이 SwiftUI를 아직 지원하지 않는 라이브러리도 SwiftUI에서 사용할 수 있습니다.
몇 가지 단점도 있습니다.
- SwiftUI는 iOS 13 이상에서만 사용할 수 있습니다.
- Xcode 미리보기에서는 뷰 계층 구조를 검사할 수 없습니다.
SwiftUI 상태 및 데이터 흐름
SwiftUI는 선언적 접근 방식을 사용하여 UI를 만드는 새로운 방법을 제공합니다. SwiftUI에 뷰가 다양한 다양한 상태와 함께 어떻게 표시되는지 확인할 수 있으며, 나머지는 시스템에서 자동으로 처리합니다. SwiftUI는 이벤트 또는 사용자 작업에 의해 기본 상태가 변경될 때마다 뷰 업데이트를 처리합니다. 이러한 설계를 일반적으로 단방향 데이터 흐름이라고 합니다. 이 설계의 세부사항은 이 Codelab에서 다루지 않지만, Apple의 상태 및 데이터 흐름 문서에서 이 내용이 어떻게 작동하는지 읽어보는 것이 좋습니다.
UIViewRepresentable 또는 UIViewControllerRepresentable를 사용하여 UIKit 및 SwiftUI 연결
iOS용 Maps SDK는 UIKit를 기반으로 빌드되지만 아직 SwiftUI 호환 뷰를 제공하지 않으므로 SwiftUI에서 사용하려면 UIViewRepresentable 또는 UIViewControllerRepresentable를 준수해야 합니다. 이러한 프로토콜을 사용하면 SwiftUI에 각각 UIKit 빌드 UIView 및 UIViewController를 포함할 수 있습니다. 두 프로토콜을 사용하여 SwiftUI 뷰에 Google 지도를 추가할 수 있지만, 다음 단계에서는 UIViewControllerRepresentable를 사용하여 지도를 포함하는 UIViewController를 살펴보겠습니다.
6. 지도 추가
이 섹션에서는 SwiftUI 뷰에 Google 지도를 추가합니다.

API 키 추가하기
이전 단계에서 만든 API 키를 iOS용 Maps SDK에 제공해야 계정을 앱에 표시되는 지도와 연결할 수 있습니다.
API 키를 제공하려면 AppDelegate.swift 파일을 탐색하고 application(_, didFinishLaunchingWithOptions) 메서드로 이동합니다. 현재 SDK는 'YOUR_API_KEY' 문자열로 GMSServices.provideAPIKey()를 통해 초기화됩니다. 이 문자열을 API 키로 바꿉니다. 이 단계를 완료하면 애플리케이션이 시작될때 iOS용 Maps SDK가 초기화됩니다.
MapViewControllerBridge를 사용하여 Google 지도 추가
이제 API 키가 SDK에 제공되고 있으므로 다음 단계에서는 앱에 지도를 표시합니다.
시작 코드에 제공된 뷰 컨트롤러 MapViewController에는 현재 뷰에 GMSMapView가 포함되어 있습니다. 그러나 이 뷰 컨트롤러는 UIKit에서 생성되었으므로 이 클래스를 ContentView 내에서 사용할 수 있도록 이 클래스를 SwiftUI에 연결해야 합니다. 방법은 다음과 같습니다.
- Xcode에서 MapViewControllerBridge파일을 엽니다.
이 클래스는 SwiftUI 뷰로 사용할 수 있도록 UIKit UIViewController를 래핑하는 데 필요한 프로토콜인 UIViewControllerRepresentable을 준수합니다. 즉, 이 프로토콜을 준수하면 UIKit 뷰를 SwiftUI 뷰로 연결할 수 있습니다. 이 프로토콜을 준수하려면 다음 두 가지 메서드를 구현해야 합니다.
- makeUIViewController(context)- 이 메서드는 SwiftUI에 의해 기본- UIViewController를 만듭니다. 여기에서- UIViewController를 인스턴스화하고 초기 상태를 전달합니다.
- updateUIViewController(_, context)- 이 메서드는 상태가 변경될 때마다 SwiftUI에 의해 호출됩니다. 여기에서 기본- UIViewController를 수정하여 상태 변경에 응답해야 합니다.
- MapViewController을 만듭니다.
makeUIViewController(context) 함수 내에서 새 MapViewController를 인스턴스화하고 결과로 반환합니다. 이제 MapViewControllerBridge가 다음과 같이 표시됩니다.
MapViewControllerBridge
import GoogleMaps
import SwiftUI
struct MapViewControllerBridge: UIViewControllerRepresentable {
  func makeUIViewController(context: Context) -> MapViewController {
    return MapViewController()
  }
  func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
  }
}
ContentView에서 MapViewControllerBridge 사용
이제 MapViewControllerBridge에서 MapViewController의 인스턴스를 생성하므로 다음 단계는 ContentView 내에서 이 구조체를 사용하여 지도를 표시하는 것입니다.
- Xcode에서 ContentView파일을 엽니다.
ContentView은 SceneDelegate에서 인스턴스화되며 최상위 애플리케이션 뷰를 포함합니다. 지도가 이 파일 내에서 추가됩니다.
- body속성 내에- MapViewControllerBridge를 만듭니다.
이 파일의 body 속성 내에서 이미 ZStack이 제공 및 구현되어 있습니다. 현재 ZStack에는 이후 단계에서 사용할 수 있도록 드래그할 수 있는 드래그 가능한 도시 목록이 포함되어 있습니다. 지금은 ZStack 내에서 MapViewControllerBridge를 ZStack의 첫 번째 하위 뷰로 만들어 도시 보기 목록 뒤의 앱에 지도를 표시합니다. 이렇게 하면 ContentView 내의 body 속성 콘텐츠가 다음과 같이 표시됩니다.
콘텐츠 보기
var body: some View {
  let scrollViewHeight: CGFloat = 80
  GeometryReader { geometry in
    ZStack(alignment: .top) {
      // Map
      MapViewControllerBridge()
      // Cities List
      CitiesList(markers: $markers) { (marker) in
        guard self.selectedMarker != marker else { return }
        self.selectedMarker = marker
        self.zoomInCenter = false
        self.expandList = false
      }  handleAction: {
        self.expandList.toggle()
      } // ...
    }
  }
}
- 이제 앱을 실행합니다. 이제 화면 하단에 드래그 가능한 도시 목록과 함께 지도 화면이 지도 화면에 표시됩니다.
7. 지도에 마커 추가하기
이전 단계에서는 도시 목록을 표시하는 상호작용 가능한 목록과 함께 지도를 추가했습니다. 이 섹션에서는 목록의 각 도시에 마커를 추가합니다.

상태 표시
ContentView현재markers 이것은 목록에 있는GMSMarker 선언되는 각 도시를cities 정적 속성입니다. 이 속성은 SwiftUI 속성 래퍼 상태로 주석 처리되어 SwiftUI에서 관리해야 함을 나타냅니다. 따라서 이 속성에서 마커가 추가되거나 삭제되는 등 변경사항이 감지되면 이 상태를 사용하는 뷰가 업데이트됩니다.
콘텐츠 보기
  static let cities = [
    City(name: "San Francisco", coordinate: CLLocationCoordinate2D(latitude: 37.7576, longitude: -122.4194)),
    City(name: "Seattle", coordinate: CLLocationCoordinate2D(latitude: 47.6131742, longitude: -122.4824903)),
    City(name: "Singapore", coordinate: CLLocationCoordinate2D(latitude: 1.3440852, longitude: 103.6836164)),
    City(name: "Sydney", coordinate: CLLocationCoordinate2D(latitude: -33.8473552, longitude: 150.6511076)),
    City(name: "Tokyo", coordinate: CLLocationCoordinate2D(latitude: 35.6684411, longitude: 139.6004407))
  ]
  /// State for markers displayed on the map for each city in `cities`
  @State var markers: [GMSMarker] = cities.map {
    let marker = GMSMarker(position: $0.coordinate)
    marker.title = $0.name
    return marker
  }
ContentView는 markers 속성을 사용하여 도시를 CitiesList 클래스에 전달하여 도시 목록을 렌더링합니다.
도시 목록
struct CitiesList: View {
  @Binding var markers: [GMSMarker]
  var body: some View {
    GeometryReader { geometry in
      VStack(spacing: 0) {
        // ...
        // List of Cities
        List {
          ForEach(0..<self.markers.count) { id in
            let marker = self.markers[id]
            Button(action: {
              buttonAction(marker)
            }) {
              Text(marker.title ?? "")
            }
          }
        }.frame(maxWidth: .infinity)
      }
    }
  }
}
결합을 통해 State를 MapViewControllerBridge로 전달
markers 속성의 데이터를 표시하는 도시 목록 외에도 이 속성을 지도에 표시하는 데 사용할 수 있도록 이 속성을 MapViewControllerBridge 구조체에 전달합니다. 다음 안내를 따르세요.
- MapViewControllerBridge내에서- @Binding주석이 달린 새- markers속성을 선언합니다.
MapViewControllerBridge
struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
- MapViewControllerBridge에서- markers속성을 사용하도록- updateUIViewController(_, context)메서드를 업데이트합니다.
이전 단계에서 언급했듯이 updateUIViewController(_, context)는 상태가 변경될 때마다 SwiftUI에 의해 호출됩니다. 이 메서드 내에 지도를 업데이트하고자 하므로 markers에 마커를 표시합니다. 이렇게 하려면 각 마커의 map 속성을 업데이트해야 합니다. 이 단계를 완료하면 MapViewControllerBridge가 다음과 같이 표시됩니다.
import GoogleMaps
import SwiftUI
struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  func makeUIViewController(context: Context) -> MapViewController {
    return MapViewController()
  }
  func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
    // Update the map for each marker
    markers.forEach { $0.map = uiViewController.map }
  }
}
- markers속성을- ContentView에서- MapViewControllerBridge로 전달
MapViewControllerBridge에 새 속성을 추가했으므로 이제 이 속성의 값을 MapViewControllerBridge의 초기화 프로그램에 전달해야 합니다. 따라서 앱을 빌드하려고 하면 컴파일되지 않는 것을 확인할 수 있습니다. 이 문제를 해결하려면 다음과 같이 MapViewControllerBridge가 만들어진 ContentView를 업데이트하고 markers 속성을 전달합니다.
struct ContentView: View {
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        MapViewControllerBridge(markers: $markers)
        // ...
      }
    }
  }
}
접두사 $는 결합된 속성을 예상하므로 markers를 MapViewControllerBridge에 전달하는 데 사용되었습니다. $는 Swift 속성 래퍼와 함께 사용하기 위해 예약된 프리픽스입니다. 상태에 적용되면 Binding이 반환됩니다.
- 앱을 실행하여 지도에 표시된 마커를 확인하세요.
8. 선택한 도시에 애니메이션 적용
이전 단계에서는 SwiftUI 보기에서 다른 SwiftUI로 상태를 전달하여 지도에 마커를 추가했습니다. 이 단계에서는 상호작용 가능한 목록에서 탭한 도시/마커에 애니메이션을 적용합니다. 애니메이션을 실행하려면 변경 시 지도의 카메라 위치를 수정하여 상태에 변경사항이 반응해야 합니다. 지도 카메라 개념에 대해 자세히 알아보려면 카메라 및 뷰를 참고하세요.

선택한 도시에 지도 애니메이션 적용
선택한 도시로 지도에 애니메이션을 적용하는 방법은 다음과 같습니다.
- MapViewControllerBridge에서 새 결합 정의해 주세요
ContentView에는 nil로 초기화되고 목록에서 도시가 선택될 때마다 업데이트되는 selectedMarker라는 State 속성이 있습니다. ContentView 내의 CitiesList 뷰 buttonAction에서 처리합니다.
콘텐츠 보기
CitiesList(markers: $markers) { (marker) in
  guard self.selectedMarker != marker else { return }
  self.selectedMarker = marker
  // ...
}
selectedMarker가 변경될 때마다 MapViewControllerBridge는 선택한 마커에 지도를 애니메이션 처리할 수 있도록 상태 변경을 알려야 합니다. 따라서 MapViewControllerBridge 유형의 GMSMarker 내에서 새 결합을 정의하고 속성 이름을 selectedMarker로 지정합니다.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
- selectedMarker가 변경될 때마다 지도에 애니메이션을 적용하기 위해- MapViewControllerBridge를 업데이트합니다.
새 결합이 선언되면 지도가 선택한 마커로 애니메이션 처리되도록 MapViewControllerBridge의 updateUIViewController_, context) 함수를 업데이트해야 합니다. 아래 코드를 복사하여 복사하세요.
struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
  func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
    markers.forEach { $0.map = uiViewController.map }
    selectedMarker?.map = uiViewController.map
    animateToSelectedMarker(viewController: uiViewController)
  }
  private func animateToSelectedMarker(viewController: MapViewController) {
    guard let selectedMarker = selectedMarker else {
      return
    }
    let map = viewController.map
    if map.selectedMarker != selectedMarker {
      map.selectedMarker = selectedMarker
      DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        map.animate(toZoom: kGMSMinZoomLevel)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
          map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
          DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
            map.animate(toZoom: 12)
          })
        }
      }
    }
  }
}
animateToSelectedMarker(viewController) 함수는 GMSMapView의 animate(with) 함수를 사용하여 일련의 지도 애니메이션 작업을 실행합니다.
- ContentView의- selectedMarker를- MapViewControllerBridge에 전달
MapViewControllerBridge에서 새 결합을 선언했으면 ContentView를 업데이트하여 MapViewControllerBridge가 인스턴스화된 selectedMarker를 전달합니다.
콘텐츠 보기
struct ContentView: View {
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker)
        // ...
      }
    }
  }
}
이제 이 단계를 완료하면 목록에서 새 도시가 선택될 때마다 지도에 애니메이션이 적용됩니다.
SwiftUI 뷰에 애니메이션을 적용하여 도시 강조
SwiftUI는 상태 전환을 위한 애니메이션 처리를 지원하므로 매우 쉽게 뷰를 애니메이션화할 수 있습니다. 이 기능을 보여주기 위해 지도 애니메이션이 완료된 후 선택한 도시에 보기를 집중시켜 애니메이션을 더 추가합니다. 이를 위해 다음 단계를 완료합니다.
- MapViewControllerBridge클로저- onAnimationEnded추가
SwiftUI 애니메이션은 이전에 추가한 지도 애니메이션 시퀀스 이후에 실행되므로 MapViewControllerBridge 내에서 onAnimationEnded이라는 새 클로저를 선언하고 animateToSelectedMarker(viewController) 내의 마지막 지도 애니메이션 후 0.5초 후에 이 클로저를 호출합니다. 메서드를 호출합니다.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
    var onAnimationEnded: () -> ()
    private func animateToSelectedMarker(viewController: MapViewController) {
    guard let selectedMarker = selectedMarker else {
      return
    }
    let map = viewController.map
    if map.selectedMarker != selectedMarker {
      map.selectedMarker = selectedMarker
      DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        map.animate(toZoom: kGMSMinZoomLevel)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
          map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
          DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
            map.animate(toZoom: 12)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
              // Invoke onAnimationEnded() once the animation sequence completes
              onAnimationEnded()
            })
          })
        }
      }
    }
  }
}
- MapViewControllerBridge에- onAnimationEnded을 구현합니다.
MapViewControllerBridge 내에서 ContentView를 인스턴스화하는 onAnimationEnded 클로저를 구현합니다. 다음 코드를 복사하여 붙여넣고 zoomInCenter라는 새 상태를 추가합니다. 또한 clipShape를 사용하여 뷰를 수정하고 잘린 도형의 지름이 zoomInCenter 값에 따라 달라집니다.
콘텐츠 보기
struct ContentView: View {
  @State var zoomInCenter: Bool = false
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        let diameter = zoomInCenter ? geometry.size.width : (geometry.size.height * 2)
        MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker, onAnimationEnded: {
          self.zoomInCenter = true
        })
        .clipShape(
           Circle()
             .size(
               width: diameter,
               height: diameter
             )
             .offset(
               CGPoint(
                 x: (geometry.size.width - diameter) / 2,
                 y: (geometry.size.height - diameter) / 2
               )
             )
        )
        .animation(.easeIn)
        .background(Color(red: 254.0/255.0, green: 1, blue: 220.0/255.0))
      }
    }
  }
}
- 앱을 실행하여 애니메이션을 확인하세요.
9. SwiftUI 이벤트 보내기
이 단계에서는 GMSMapView에서 내보낸 이벤트를 수신 대기하고 이 이벤트를 SwiftUI로 전송합니다. 특히 대리자가 지도뷰에 설정하고 카메라 이동 이벤트를 수신 대기하여 도시의 초점이 맞춰지고 지도 카메라가 동작에서 나오면 지도뷰에서 초점이 해제되어 지도를 더 많이 볼 수 있습니다.
SwiftUI 코디네이터 사용
GMSMapView는 카메라 위치 변경이나 마커를 탭할 때와 같은 이벤트를 내보냅니다. 이러한 이벤트를 수신 대기하는 메커니즘은 GMSMapViewDelegate 프로토콜을 통해 이루어집니다. SwiftUI는 UIKit 뷰 컨트롤러의 대리자 역할을 하는 데 사용되는 코디네이터의 개념을 도입합니다. 따라서 SwiftUI 환경에서는 코디네이터가 GMSMapViewDelegate 프로토콜을 준수해야 합니다. 이렇게 하려면 다음 단계를 완료해야 합니다.
- MapViewControllerBridge내에- MapViewCoordinator라는 코디네이터를 만듭니다.
MapViewControllerBridge 클래스 내에 중첩 클래스를 만들고 이름을 MapViewCoordinator로 지정합니다. 이 클래스는 GMSMapViewDelegate를 준수해야 하며 MapViewControllerBridge를 속성으로 선언해야 합니다.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    var mapViewControllerBridge: MapViewControllerBridge
    init(_ mapViewControllerBridge: MapViewControllerBridge) {
      self.mapViewControllerBridge = mapViewControllerBridge
    }
  }
}
- MapViewControllerBridge에- makeCoordinator()을 구현합니다.
그런 다음 MapViewControllerBridge 내에 makeCoordinator() 메서드를 구현하고 이전 단계에서 만든 MapViewCoodinator 인스턴스를 반환합니다.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator(self)
  }
}
- MapViewCoordinator를 지도뷰의 대리자로 설정
맞춤 코디네이터를 만든 후 다음 단계는 코디네이터를 뷰 컨트롤러의 지도뷰의 대리자로 설정하는 것입니다. 이렇게 하려면 makeUIViewController(context)에서 뷰 컨트롤러 초기화를 업데이트합니다. 이전 단계에서 만든 코디네이터는 Context 객체에서 액세스할 수 있습니다.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeUIViewController(context: Context) -> MapViewController {
    let uiViewController = MapViewController()
    uiViewController.map.delegate = context.coordinator
    return uiViewController
  }
- 카메라가 움직이는 이벤트를 전파할 수 있도록 MapViewControllerBridge에 클로저를 추가합니다.
카메라 이동으로 뷰를 업데이트하는 것이 목표이므로 MapViewControllerBridge 내에서 mapViewWillMove라는 부울을 허용하는 새 닫는 속성을 선언하고 MapViewCoordinator 내의 대리자 메서드 mapView(_, willMove)에서 이 클로저를 호출합니다. SwiftUI 뷰가 동작 관련 카메라 이동 이벤트에만 반응할 수 있도록 gesture 값을 클로저에 전달합니다.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
  var mapViewWillMove: (Bool) -> ()
  //...
  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    // ...
    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
      self.mapViewControllerBridge.mapViewWillMove(gesture)
    }
  }
}
- mapWillMove값을 전달하려면 ContentView 업데이트
MapViewControllerBridge에 새 클로저가 선언되었으므로 ContentView를 업데이트하여 이 새 클로저의 값을 전달합니다. 이 클로저 내에서 이동 이벤트가 동작과 관련된 경우 상태 zoomInCenter를 false로 전환합니다. 그러면 동작으로 지도가 이동할 때 지도가 전체 보기로 다시 표시됩니다.
콘텐츠 보기
struct ContentView: View {
  @State var zoomInCenter: Bool = false
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        let diameter = zoomInCenter ? geometry.size.width : (geometry.size.height * 2)
        MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker, onAnimationEnded: {
          self.zoomInCenter = true
        }, mapViewWillMove: { (isGesture) in
          guard isGesture else { return }
          self.zoomInCenter = false
        })
        // ...
      }
    }
  }
}
- 앱을 실행하여 새 변경사항을 확인하세요.
10. 축하합니다
여기까지 오신 여러분을 축하합니다. 많은 내용을 살펴봤습니다. 이제 배운 내용을 바탕으로 iOS용 Maps SDK를 사용하여 SwiftUI 앱을 직접 빌드할 수 있기를 바랍니다.
학습한 내용
- SwiftUI와 UIKit의 차이점
- UIViewControllerRepresentable을 사용하여 SwiftUI와 UIKit를 연결하는 방법
- 지도뷰를 변경하는 방법구/군/시 및바인딩
- 코디네이터를 사용하여 지도뷰에서 SwiftUI로 이벤트를 전송하는 방법
다음 단계
- iOS용 Maps SDK - iOS용 Maps SDK 공식 문서
- iOS용 Places SDK - 주변의 지역 비즈니스 및 관심 장소를 찾습니다.
- maps-sdk-for-ios-samples - iOS용 Maps SDK 내의 모든 기능을 보여주는 GitHub의 샘플 코드입니다.
- SwiftUI - SwiftUI에 관한 Apple의 공식 문서
- 아래 질문에 답하여 Google에서 가장 유용한 콘텐츠를 만들 수 있도록 도와주세요.
다른 Codelab에서 어떤 내용을 다뤘으면 하시나요?
원하는 Codelab이 위에 나와 있지 않나요? 여기에서 새로운 문제로 요청하기