1. Trước khi bắt đầu
Lớp học lập trình này hướng dẫn bạn cách sử dụng SDK Maps dành cho iOS bằng SwiftUI.
Điều kiện tiên quyết
- Kiến thức cơ bản về Swift
- Quen thuộc cơ bản với SwiftUI
Bạn sẽ thực hiện
- Bật và sử dụng SDK Maps dành cho iOS để thêm Google Maps vào ứng dụng iOS bằng SwiftUI.
- Thêm điểm đánh dấu vào bản đồ.
- Chuyển trạng thái từ chế độ xem SwiftUI sang đối tượng
GMSMapView
và ngược lại.
Bạn cần có
- Xcode 11.0 trở lên
- Tài khoản Google có bật tính năng thanh toán
- SDK Maps dành cho iOS
- agethage
2. Bắt đầu thiết lập
Bật bước Maps SDK cho iOS cho bước bật sau đây.
Thiết lập Nền tảng Google Maps
Nếu bạn chưa có tài khoản Google Cloud Platform và một dự án đã bật tính năng thanh toán, vui lòng xem hướng dẫn Bắt đầu sử dụng Google Maps Platform để tạo tài khoản thanh toán và một dự án.
- Trong Cloud Console, hãy nhấp vào trình đơn thả xuống dự án và chọn dự án mà bạn muốn sử dụng cho lớp học lập trình này.
- Bật API và SDK của Nền tảng Google Maps bắt buộc cho lớp học lập trình này trong Google Cloud Marketplace. Để làm như vậy, hãy làm theo các bước trong video này hoặc tài liệu này.
- Tạo khoá API trong trang Thông tin xác thực của Cloud Console. Bạn có thể làm theo các bước trong video này hoặc tài liệu này. Tất cả các yêu cầu gửi đến Google Maps Platform đều yêu cầu khóa API.
3. Tải mã bắt đầu
Để bắt đầu nhanh nhất có thể, hãy tham khảo khóa học lập trình này để tham khảo một số mã dành cho người mới bắt đầu. Bạn có thể chuyển sang giải pháp này, nhưng nếu muốn làm theo tất cả các bước để tự xây dựng giải pháp, hãy đọc tiếp.
- Sao chép kho lưu trữ nếu bạn đã cài đặt
git
.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git
Ngoài ra, bạn có thể nhấp vào nút sau để tải mã nguồn xuống.
- Khi nhận được mã, ở thiết bị đầu cuối
cd
vào phòng thử nghiệmstarter/GoogleMapsSwiftUI
. - Chạy
carthage update --platform iOS
để tải SDK Maps xuống cho iOS - Cuối cùng, hãy mở tệp
GoogleMapsSwiftUI.xcodeproj
trong Xcode
4. Tổng quan về mã
Trong dự án dành cho người mới bắt đầu mà bạn đã tải xuống, các lớp sau đây đã được cung cấp và triển khai cho bạn:
AppDelegate
–UIApplicationDelegate
của ứng dụng. Đây là nơi SDK Maps dành cho iOS sẽ được khởi tạo.City
- một cấu trúc đại diện cho một thành phố (chứa tên và tọa độ của thành phố).MapViewController
– một UIKitUIViewController
đơn giản chứa Google Maps (GMSMapView)SceneDelegate
– ứng dụngUIWindowSceneDelegate
mà từ đóContentView
được tạo bản sao.
Ngoài ra, các lớp sau đây có một phần triển khai và sẽ được bạn hoàn thành vào cuối khóa học này:
ContentView
– chế độ xem SwiftUI cấp cao nhất chứa ứng dụng của bạn.MapViewControllerBridge
– một lớp kết nối chế độ xem UIKit với chế độ xem SwiftUI. Cụ thể, đây là lớp sẽ giúp bạn có thể truy cập vàoMapViewController
trong SwiftUI.
5. Sử dụng SwiftUI và UIKit
SwiftUI đã được giới thiệu trong iOS 13 như một khung giao diện người dùng thay thế trên UIKit để phát triển các ứng dụng iOS. So với phiên bản UIKit trước đây, SwiftUI mang lại một số lợi thế. Chẳng hạn như:
- Chế độ xem tự động cập nhật khi trạng thái thay đổi. Khi sử dụng các đối tượng có tên là Trạng thái, mọi thay đổi đối với giá trị cơ bản mà đối tượng đó chứa sẽ khiến giao diện người dùng tự động cập nhật.
- Tính năng xem trước trực tiếp giúp phát triển nhanh hơn. Bản xem trước trực tiếp giảm thiểu nhu cầu xây dựng và triển khai mã cho trình mô phỏng để xem các thay đổi trực quan dưới dạng bản xem trước của chế độ xem SwiftUI.
- Nguồn của sự kiện là trong Swift. Tất cả chế độ xem trong SwiftUI đều được khai báo trong Swift. Do đó, bạn không cần phải sử dụng Trình tạo giao diện nữa.
- Tương tác với UIKit. Khả năng tương tác với UIKit đảm bảo rằng các ứng dụng hiện có có thể sử dụng SwiftUI tăng dần với chế độ xem hiện có. Ngoài ra, bạn vẫn có thể sử dụng các thư viện chưa hỗ trợ SwiftUI, chẳng hạn như SDK Maps dành cho iOS, trong SwiftUI.
Cũng có một số hạn chế:
- SwiftUI chỉ có trên iOS 13 trở lên.
- Không thể kiểm tra hệ phân cấp chế độ xem trong bản xem trước Xcode.
Trạng thái và luồng dữ liệu của SwiftUI
SwiftUI cung cấp một cách mới để tạo giao diện người dùng bằng phương pháp khai báo – bạn cho SwiftUI biết cách bạn muốn chế độ xem của mình nhìn vào tất cả các trạng thái khác nhau cho nó và hệ thống sẽ thực hiện các phần còn lại. SwiftUI xử lý việc cập nhật chế độ xem bất cứ khi nào trạng thái cơ bản thay đổi do một sự kiện hoặc hành động của người dùng. Thiết kế này thường được gọi là luồng dữ liệu một chiều. Mặc dù chi tiết cụ thể của thiết kế này nằm ngoài phạm vi của lớp học lập trình này, nhưng bạn nên đọc về cách hoạt động của thiết kế này trong tài liệu về Trạng thái và luồng dữ liệu của Apple.
Kết nối UIKit và SwiftUI bằng cách sử dụng UIViewRepresentationable hoặc UIViewControllerRepresentable
Vì SDK Maps dành cho iOS được xây dựng dựa trên UIKit, và chưa cung cấp chế độ xem tương thích với SwiftUI, nên việc sử dụng SDK trong SwiftUI yêu cầu phải tuân thủ UIViewRepresentable
hoặc UIViewControllerRepresentable
. Các giao thức này cho phép SwiftUI bao gồm UIView
và UIViewController
do UIKit tạo tương ứng. Mặc dù bạn có thể sử dụng một trong hai giao thức để thêm Google Maps vào chế độ xem SwiftUI, nhưng trong bước tiếp theo, chúng ta sẽ xem xét bằng cách sử dụng UIViewControllerRepresentable
để bao gồm UIViewController
chứa bản đồ.
6. Thêm bản đồ
Trong phần này, bạn sẽ thêm Google Maps vào chế độ xem SwiftUI.
Thêm khóa API của bạn
Khóa API mà bạn đã tạo ở bước trước đó cần được cung cấp cho SDK Maps dành cho iOS để liên kết tài khoản của bạn với bản đồ sẽ được hiển thị trên ứng dụng.
Để cung cấp khóa API của bạn, hãy mở tệp AppDelegate.swift
và chuyển đến phương thức application(_, didFinishLaunchingWithOptions)
. Hiện tại, SDK được khởi tạo qua GMSServices.provideAPIKey()
với chuỗi " THIS_API_KEY". Thay thế chuỗi đó bằng khóa API của bạn. Khi hoàn tất bước này, SDK Maps sẽ khởi chạy cho iOS khi ứng dụng khởi chạy.
Thêm Bản đồ Google bằng MapViewControllerBridge
Giờ đây, bạn đã cung cấp khóa API của mình cho SDK, bước tiếp theo là hiển thị bản đồ trên ứng dụng.
Bộ điều khiển chế độ xem được cung cấp trong mã dành cho người mới bắt đầu, MapViewController
hiện chứa một GMSMapView
trong chế độ xem. Tuy nhiên, vì bộ điều khiển chế độ xem này được tạo trong UIKit, bạn sẽ cần kết nối lớp này với SwiftUI để có thể sử dụng lớp này trong ContentView
. Cách thực hiện:
- Mở tệp
MapViewControllerBridge
trong Xcode.
Lớp này tuân thủ UIViewControllerRepresentationable, giao thức cần thiết để bao bọc một UIKit UIViewController
để có thể dùng làm giao diện SwiftUI. Nói cách khác, việc tuân thủ giao thức này cho phép bạn cầu nối một chế độ xem UIKit vào chế độ xem SwiftUI. Việc tuân thủ giao thức này yêu cầu bạn phải triển khai hai phương thức:
makeUIViewController(context)
– phương thức này được SwiftUI gọi để tạoUIViewController
cơ bản. Đây là nơi bạn sẽ tạoUIViewController
và chuyển trạng thái ban đầu của thiết bị.updateUIViewController(_, context)
– phương thức này được SwiftUI gọi bất cứ khi nào trạng thái thay đổi. Đây là nơi bạn sẽ thực hiện bất kỳ sửa đổi nào đối vớiUIViewController
cơ bản để phản ứng với sự thay đổi trạng thái.
- Tạo một
MapViewController
Bên trong hàm makeUIViewController(context)
, hãy tạo MapViewController
mới và trả về kết quả đó. Sau khi làm như vậy, MapViewControllerBridge
của bạn sẽ trông giống như sau:
MapViewControllerbridge
import GoogleMaps
import SwiftUI
struct MapViewControllerBridge: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MapViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
}
}
Sử dụng MapViewControllerBridge trong ContentView
Bây giờ, MapViewControllerBridge
đang tạo một phiên bản của MapViewController
, bước tiếp theo là sử dụng cấu trúc này trong ContentView
để hiển thị bản đồ.
- Mở tệp
ContentView
trong Xcode.
ContentView
được tạo bản sao trong SceneDelegate
và chứa chế độ xem ứng dụng cấp cao nhất. Bản đồ sẽ được thêm từ trong tệp này.
- Tạo
MapViewControllerBridge
trong thuộc tínhbody
.
Trong thuộc tính body
của tệp này, ZStack
đã được cung cấp và triển khai cho bạn. ZStack
hiện chứa danh sách các thành phố có thể kéo và tương tác mà bạn sẽ sử dụng trong bước tiếp theo. Hiện tại, trong ZStack
, hãy tạo MapViewControllerBridge
làm chế độ xem con đầu tiên của ZStack
để bản đồ sẽ hiển thị trong ứng dụng phía sau danh sách thành phố. Khi làm như vậy, nội dung của thuộc tính body
trong ContentView
sẽ có dạng như sau:
Chế độ xem nội dung
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()
} // ...
}
}
}
- Bây giờ, hãy tiếp tục và chạy ứng dụng. Bây giờ, bạn sẽ thấy tải bản đồ trên màn hình của thiết bị cùng với danh sách thành phố có thể kéo ở cuối màn hình.
7. Thêm điểm đánh dấu vào bản đồ
Trong bước trước đó, bạn đã thêm bản đồ cùng với danh sách có thể tương tác hiển thị danh sách các thành phố. Trong phần này, bạn sẽ thêm điểm đánh dấu cho từng thành phố trong danh sách đó.
Điểm đánh dấu là Trạng thái
ContentView
hiện khai báo một thuộc tính có tên là markers
, đây là danh sách GMSMarker
đại diện cho mỗi thành phố được khai báo trong thuộc tính tĩnh cities
. Xin lưu ý rằng thuộc tính này được chú thích bằng trình bao bọc thuộc tính SwiftUI State để cho biết rằng thuộc tính này sẽ do SwiftUI quản lý. Do đó, nếu phát hiện thấy bất kỳ thay đổi nào với thuộc tính này, chẳng hạn như thêm hoặc xóa một điểm đánh dấu, thì các chế độ xem sử dụng trạng thái này sẽ được cập nhật.
Chế độ xem nội dung
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
}
Xin lưu ý rằng ContentView
sử dụng thuộc tính markers
để hiển thị danh sách các thành phố bằng cách chuyển thuộc tính này đến lớp CitiesList
.
Danh sách thành phố
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)
}
}
}
}
Chuyển Trạng thái đến MapViewControllerBridge qua Binding
Ngoài danh sách thành phố hiển thị dữ liệu từ tài sản markers
, hãy chuyển tài sản này đến cấu trúc MapViewControllerBridge
để có thể sử dụng tài sản đó để hiển thị những điểm đánh dấu đó trên bản đồ. Cách làm như sau:
- Khai báo một tài sản
markers
mới trongMapViewControllerBridge
được chú thích bằng@Binding
MapViewControllerbridge
struct MapViewControllerBridge: : UIViewControllerRepresentable {
@Binding var markers: [GMSMarker]
// ...
}
- Trong
MapViewControllerBridge
, hãy cập nhật phương thứcupdateUIViewController(_, context)
để sử dụng thuộc tínhmarkers
Như đã đề cập trong bước trước, updateUIViewController(_, context)
sẽ được SwiftUI gọi bất cứ khi nào trạng thái thay đổi. Trong phương pháp này, chúng ta muốn cập nhật bản đồ để hiển thị các điểm đánh dấu trong markers
. Để làm điều này, bạn cần cập nhật thuộc tính map
của mỗi điểm đánh dấu. Sau khi hoàn tất bước này, MapViewControllerBridge
của bạn sẽ có dạng như sau:
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 }
}
}
- Chuyển thuộc tính
markers
từContentView
sangMapViewControllerBridge
Vì bạn đã thêm một thuộc tính mới trong MapViewControllerBridge
, nên bây giờ, yêu cầu phải chuyển giá trị cho thuộc tính này vào trình khởi tạo cho MapViewControllerBridge
. Vì vậy, nếu cố gắng tạo ứng dụng, bạn sẽ nhận thấy rằng ứng dụng sẽ không biên dịch. Để khắc phục lỗi này, hãy cập nhật ContentView
khi MapViewControllerBridge
được tạo và chuyển vào thuộc tính markers
như sau:
struct ContentView: View {
// ...
var body: some View {
// ...
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
MapViewControllerBridge(markers: $markers)
// ...
}
}
}
}
Lưu ý: tiền tố $
được dùng để chuyển vào markers
thành MapViewControllerBridge
vì cần có thuộc tính liên kết. $
là một tiền tố đã được đặt trước để dùng với trình bao bọc thuộc tính Swift. Khi được áp dụng cho một Tiểu bang, hàm này sẽ trả về Liên kết.
- Hãy tiếp tục và chạy ứng dụng để xem các điểm đánh dấu được hiển thị trên bản đồ.
8. Hoạt hình cho một thành phố đã chọn
Ở bước trước, bạn đã thêm điểm đánh dấu vào bản đồ bằng cách chuyển Trạng thái từ chế độ xem SwiftUI này sang chế độ xem khác. Trong bước này, bạn sẽ tạo ảnh động cho thành phố/điểm đánh dấu sau khi nhấn vào danh sách có thể tương tác. Để thực hiện ảnh động, bạn sẽ phản ứng với các thay đổi thành một Trạng thái bằng cách sửa đổi vị trí máy ảnh của bản đồ khi thay đổi này xảy ra. Để tìm hiểu thêm về khái niệm máy ảnh của bản đồ, hãy xem Máy ảnh và chế độ xem.
Hoạt hình bản đồ đến thành phố đã chọn
Để tạo bản đồ cho một thành phố được chọn:
- Xác định liên kết mới trong
MapViewControllerBridge
ContentView
có một thuộc tính Tiểu bang là selectedMarker
được khởi tạo thành giá trị nil và được cập nhật bất cứ khi nào một thành phố được chọn trong danh sách. Việc này được xử lý trong chế độ xem CitiesList
buttonAction
trong ContentView
.
Chế độ xem nội dung
CitiesList(markers: $markers) { (marker) in
guard self.selectedMarker != marker else { return }
self.selectedMarker = marker
// ...
}
Bất cứ khi nào selectedMarker
thay đổi, MapViewControllerBridge
cần biết về trạng thái này để có thể tạo bản đồ cho điểm đánh dấu được chọn. Do đó, hãy xác định một Liên kết mới trong MapViewControllerBridge
thuộc loại GMSMarker
và đặt tên cho thuộc tính selectedMarker
.
MapViewControllerbridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var selectedMarker: GMSMarker?
}
- Cập nhật
MapViewControllerBridge
để tạo hiệu ứng cho bản đồ bất cứ khi nàoselectedMarker
thay đổi
Sau khi liên kết mới được khai báo, bạn cần cập nhật hàm updateUIViewController_, context)
của MapViewControllerBridge
để bản đồ đó chuyển động tới điểm đánh dấu đã chọn. Hãy tiếp tục và thực hiện bằng cách sao chép mã bên dưới:
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)
})
}
}
}
}
}
Hàm animateToSelectedMarker(viewController)
sẽ thực hiện một chuỗi các ảnh động bằng bản đồ bằng hàm animate(with)
của GMSMapView
.
- Chuyển
ContentView
\39;sselectedMarker
tớiMapViewControllerBridge
Sau khi MapViewControllerBridge
đã khai báo Binding mới, hãy tiếp tục và cập nhật ContentView
để chuyển vào selectedMarker
nơi MapViewControllerBridge
được tạo bản sao.
Chế độ xem nội dung
struct ContentView: View {
// ...
var body: some View {
// ...
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker)
// ...
}
}
}
}
Khi hoàn tất bước này, bản đồ sẽ được tạo một bản đồ bất cứ khi nào một thành phố mới được chọn trong danh sách.
Hoạt ảnh chế độ xem SwiftUI để nhấn mạnh thành phố
SwiftUI giúp hoạt ảnh chế độ xem rất dễ dàng vì nó sẽ xử lý các hiệu ứng động cho quá trình chuyển đổi Trạng thái. Để minh họa điều này, bạn sẽ thêm các ảnh động khác bằng cách tập trung chế độ xem vào thành phố đã chọn sau khi ảnh động bản đồ hoàn tất. Để làm việc này, hãy hoàn tất các bước sau:
- Thêm trạng thái đóng cửa
onAnimationEnded
vàoMapViewControllerBridge
Vì ảnh động SwiftUI sẽ được thực hiện sau trình tự hoạt ảnh bản đồ mà bạn đã thêm trước đó, hãy khai báo trạng thái đóng mới có tên là onAnimationEnded
trong MapViewControllerBridge
và gọi lệnh đóng này sau độ trễ 0,5 giây sau ảnh động cuối cùng trong bản đồ trong phương thức animateToSelectedMarker(viewController)
.
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()
})
})
}
}
}
}
}
- Triển khai
onAnimationEnded
trongMapViewControllerBridge
Triển khai lệnh đóng onAnimationEnded
khi MapViewControllerBridge
được tạo trong ContentView
. Sao chép và dán mã sau để thêm Trạng thái mới có tên là zoomInCenter
và cũng sửa đổi chế độ xem bằng cách sử dụng clipShape
và thay đổi đường kính của hình dạng bị cắt tùy thuộc vào giá trị của zoomInCenter
Chế độ xem nội dung
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))
}
}
}
}
- Hãy tiếp tục và chạy ứng dụng này để xem ảnh động!
9. Gửi sự kiện đến SwiftUI
Trong bước này, bạn sẽ nghe các sự kiện phát ra từ GMSMapView
và gửi sự kiện đó đến SwiftUI. Cụ thể, bạn sẽ đặt một đại biểu cho chế độ xem bản đồ và lắng nghe các sự kiện di chuyển máy ảnh để khi một thành phố được tập trung và máy ảnh bản đồ di chuyển từ một cử chỉ, chế độ xem bản đồ sẽ không lấy nét để bạn có thể xem thêm bản đồ.
Sử dụng trình điều phối SwiftUI
GMSMapView
phát ra các sự kiện như vị trí của máy ảnh thay đổi hoặc khi một điểm đánh dấu được nhấn vào. Cơ chế lắng nghe những sự kiện này là thông qua giao thức GMSMapViewDelegate. SwiftUI giới thiệu khái niệm Điều phối viên được sử dụng cụ thể để làm đại biểu cho các bộ điều khiển chế độ xem UIKit. Vì vậy, trong thế giới SwiftUI, Người điều phối phải chịu trách nhiệm tuân thủ giao thức GMSMapViewDelegate
. Để làm việc này, hãy hoàn tất các bước sau:
- Tạo Điều phối viên có tên là
MapViewCoordinator
trongMapViewControllerBridge
Tạo một lớp lồng nhau bên trong lớp MapViewControllerBridge
và gọi lớp đó là MapViewCoordinator
. Lớp này phải tuân thủ GMSMapViewDelegate
và phải khai báo MapViewControllerBridge
là một thuộc tính.
MapViewControllerbridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
var mapViewControllerBridge: MapViewControllerBridge
init(_ mapViewControllerBridge: MapViewControllerBridge) {
self.mapViewControllerBridge = mapViewControllerBridge
}
}
}
- Triển khai
makeCoordinator()
trongMapViewControllerBridge
Tiếp theo, hãy triển khai phương thức makeCoordinator()
trong MapViewControllerBridge
và trả về một bản sao của MapViewCoodinator
mà bạn đã tạo ở bước trước.
MapViewControllerbridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
func makeCoordinator() -> MapViewCoordinator {
return MapViewCoordinator(self)
}
}
- Đặt
MapViewCoordinator
làm đại biểu của chế độ xem bản đồ
Với cách tạo tọa độ tùy chỉnh, bước tiếp theo là đặt tọa độ làm đại biểu cho chế độ xem bản đồ của bộ điều khiển chế độ xem. Để thực hiện việc này, hãy cập nhật quá trình khởi tạo bộ điều khiển chế độ xem trong makeUIViewController(context)
. Bạn có thể truy cập tọa độ đã tạo từ bước trước đó từ đối tượng Ngữ cảnh.
MapViewControllerbridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
func makeUIViewController(context: Context) -> MapViewController {
let uiViewController = MapViewController()
uiViewController.map.delegate = context.coordinator
return uiViewController
}
- Thêm trạng thái đóng vào
MapViewControllerBridge
để máy ảnh có thể di chuyển sự kiện lên
Vì mục tiêu là cập nhật chế độ xem bằng quá trình di chuyển máy ảnh, hãy khai báo một thuộc tính đóng mới chấp nhận boolean trong MapViewControllerBridge
có tên là mapViewWillMove
và gọi lệnh đóng này trong phương thức ủy quyền mapView(_, willMove)
trong MapViewCoordinator
. Chuyển giá trị của gesture
đến trạng thái đóng để chế độ xem SwiftUI chỉ có thể phản ứng với các sự kiện di chuyển máy ảnh liên quan đến cử chỉ.
MapViewControllerbridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
var mapViewWillMove: (Bool) -> ()
//...
final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
// ...
func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
self.mapViewControllerBridge.mapViewWillMove(gesture)
}
}
}
- Cập nhật ContentView để chuyển một giá trị cho
mapWillMove
Với trạng thái đóng cửa mới được khai báo vào MapViewControllerBridge
, hãy cập nhật ContentView
để chuyển một giá trị cho trạng thái đóng cửa mới này. Trong trường hợp đóng cửa đó, hãy chuyển đổi Trạng thái zoomInCenter
thành false
nếu sự kiện di chuyển liên quan đến một cử chỉ. Thao tác này sẽ hiển thị lại bản đồ ở chế độ xem toàn bộ khi bản đồ được di chuyển bằng một cử chỉ.
Chế độ xem nội dung
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
})
// ...
}
}
}
}
- Hãy tiếp tục và chạy ứng dụng để xem các thay đổi mới!
10. Xin chúc mừng
Chúc mừng bạn đã đạt được rất nhiều bước! Bạn đã đi sâu vào nhiều kiến thức cơ bản và hy vọng những bài học đã học cho phép bạn hiện xây dựng ứng dụng SwiftUI của riêng mình bằng cách sử dụng SDK Maps dành cho iOS.
Những điều bạn đã học được
- Sự khác biệt giữa SwiftUI và UIKit
- Cách kết nối giữa SwiftUI và UIKit bằng UIViewControllerRepresentable
- Cách thay đổi chế độ xem bản đồ bằng Trạng thái và Liên kết
- Cách gửi một sự kiện từ chế độ xem bản đồ đến SwiftUI bằng cách sử dụng Coordinator
Nội dung tiếp theo là gì?
- SDK Maps dành cho iOS – tài liệu chính thức về SDK Maps dành cho iOS
- SDK địa điểm cho iOS - tìm doanh nghiệp địa phương và điểm yêu thích xung quanh bạn
- maps-sdk-for-ios-samples – mã mẫu trên GitHub minh họa tất cả các tính năng trong SDK Maps dành cho iOS.
- SwiftUI – Tài liệu chính thức của Apple về SwiftUI
- Hãy giúp chúng tôi tạo nội dung mà bạn thấy hữu ích nhất bằng cách trả lời câu hỏi dưới đây:
Bạn muốn xem những lớp học lập trình nào khác?
Lớp học lập trình mà bạn muốn không được liệt kê ở trên? Yêu cầu phát hành vấn đề mới tại đây.