昨天,我們完成了顯示天氣詳情的核心功能模組。然而,使用者還無法選擇他們想看的地區。
今天,我們就要來完主入口畫面。我們將使用 MapKit
框架,打造一個以台灣地圖為基礎的互動介面,讓使用者可以直觀地在地圖上點擊城市,進而查詢該地區的天氣資訊。
困惑點:我想在 App 的主畫面加上一個地圖,並在上面標示出台灣所有主要的城市。當使用者點擊這些標示時,要如何觸發事件,並跳轉到對應城市的天氣頁面呢?
AI 協作
我希望在 App 的主畫面加上一個地圖,顯示台灣所有主要城市的大頭針。當使用者點擊大頭針時,就跳轉到該城市的天氣頁面。請問在 iOS 中,實現這個功能的核心框架是什麼?以及如何處理地圖上的點擊事件?
AI 的回答:
要為您的 iOS App 實現這個功能,核心框架是 MapKit。
核心框架:MapKit
MapKit 是 Apple 提供的原生地圖框架,它允許您在應用程式中嵌入地圖,並能自由地操作地圖資料。使用 MapKit 您可以:
- 顯示地圖,並控制其顯示區域和縮放等級。
- 添加地圖標註 (Annotations),也就是您所說的「大頭針」,來標記特定位置。
- 處理使用者與地圖的互動,例如點擊地圖或標註。
如何處理地圖上的點擊事件
處理點擊事件主要有兩個步驟:
- 添加地圖標註 (Annotations):首先,您需要為台灣所有主要城市建立相應的
MKPointAnnotation
物件,每個物件都包含該城市的座標。將這些物件添加到您的MKMapView
中,App 就會在地圖上顯示出這些「大頭針」。- 實作代理協定 (Delegate Protocol):
MKMapView
有一個代理 (delegate) 屬性,您可以將它設定為您的視圖控制器 (view controller)。當使用者點擊任何一個地圖標註時,MapKit 會呼叫代理協定中的特定方法,例如mapView(_:didSelect:)
。在這個方法中,您可以取得使用者點擊的標註資訊,並依據其所代表的城市,執行跳轉到對應天氣頁面的邏輯。簡而言之,MapKit 提供了您所需的一切工具,讓您能夠輕鬆地在地圖上展示資料並處理使用者的互動。
MainViewController
中,初始化一個 MKMapView
,並將其設定為顯示整個台灣的視圖。MKPointAnnotation
加到地圖上。MainViewController
遵守 MKMapViewDelegate
,並實作 mapView(_:didSelect:)
方法。didSelect
方法中,呼叫一個 MapsToWeather(for:)
函式,它會建立 AreaViewController
的實例,將被點擊的城市名稱傳遞過去,並以 Modal 方式呈現。MainViewController
)我們的 MainViewController
將作為 App 的入口。
MainViewController.swift
及其 .xib
檔案。MainViewController.xib
,從元件庫拖曳一個 Map Kit View
到畫布上,並設定其 Auto Layout 約束使其填滿整個安全區域。MKMapView
與 MainViewController.swift
檔案連結成 IBOutlet
,命名為 mapView
。viewDidLoad
中呼叫 setupMapView()
,設定地圖的初始狀態。檔案:Views/MainView/MainViewController.swift
import UIKit
import MapKit
class MainViewController: UIViewController {
// MARK: - IBOutlet
@IBOutlet weak var mapView: MKMapView!
// MARK: - Property
let areas = ["臺北市", "新北市", "桃園市", "臺中市", "臺南市", "高雄市", "基隆市", "新竹縣", "新竹市", "苗栗縣", "彰化縣", "南投縣", "雲林縣", "嘉義縣", "嘉義市", "屏東縣", "宜蘭縣", "花蓮縣", "臺東縣", "澎湖縣", "金門縣", "連江縣"]
let cityCoordinates: [String: CLLocationCoordinate2D] = [
"臺北市": CLLocationCoordinate2D(latitude: 25.0330, longitude: 121.5654),
"新北市": CLLocationCoordinate2D(latitude: 25.0169, longitude: 121.4628),
"桃園市": CLLocationCoordinate2D(latitude: 24.9936, longitude: 121.2908),
"臺中市": CLLocationCoordinate2D(latitude: 24.1477, longitude: 120.6736),
"臺南市": CLLocationCoordinate2D(latitude: 22.9997, longitude: 120.2133),
"高雄市": CLLocationCoordinate2D(latitude: 22.6273, longitude: 120.3014),
"基隆市": CLLocationCoordinate2D(latitude: 25.1276, longitude: 121.7392),
"新竹縣": CLLocationCoordinate2D(latitude: 24.8423, longitude: 121.0116),
"新竹市": CLLocationCoordinate2D(latitude: 24.8039, longitude: 120.9687),
"苗栗縣": CLLocationCoordinate2D(latitude: 24.5602, longitude: 120.8148),
"彰化縣": CLLocationCoordinate2D(latitude: 24.0524, longitude: 120.5161),
"南投縣": CLLocationCoordinate2D(latitude: 23.9125, longitude: 120.9328),
"雲林縣": CLLocationCoordinate2D(latitude: 23.7121, longitude: 120.4320),
"嘉義縣": CLLocationCoordinate2D(latitude: 23.4500, longitude: 120.2500),
"嘉義市": CLLocationCoordinate2D(latitude: 23.4801, longitude: 120.4491),
"屏東縣": CLLocationCoordinate2D(latitude: 22.5513, longitude: 120.5433),
"宜蘭縣": CLLocationCoordinate2D(latitude: 24.7021, longitude: 121.7378),
"花蓮縣": CLLocationCoordinate2D(latitude: 23.9871, longitude: 121.6016),
"臺東縣": CLLocationCoordinate2D(latitude: 22.7972, longitude: 121.0714),
"澎湖縣": CLLocationCoordinate2D(latitude: 23.5701, longitude: 119.5663),
"金門縣": CLLocationCoordinate2D(latitude: 24.4411, longitude: 118.3184),
"連江縣": CLLocationCoordinate2D(latitude: 26.1517, longitude: 119.9575)
]
// MARK: - LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
addAnnotations()
}
// MARK: - UI Setting
func setupMapView() {
mapView.delegate = self
mapView.mapType = .hybrid
let center = CLLocationCoordinate2D(latitude: 24.1477, longitude: 120.6736)
let span = MKCoordinateSpan(latitudeDelta: 4.0, longitudeDelta: 4.0)
let region = MKCoordinateRegion(center: center, span: span)
mapView.setRegion(region, animated: true)
}
// MARK: - Function
func addAnnotations() {
for area in areas {
if let coordinate = cityCoordinates[area] {
let annotation = MKPointAnnotation()
annotation.title = area
annotation.coordinate = coordinate
mapView.addAnnotation(annotation)
}
}
}
func navigateToWeather(for city: String) {
let areaVC = AreaViewController()
areaVC.selectedArea = city
let navVC = UINavigationController(rootViewController: areaVC)
self.present(navVC, animated: true, completion: nil)
}
}
當使用者點擊地圖上的大頭針時,我們需要捕捉這個事件,並將使用者導向對應的 AreaViewController
。
檔案:Views/MainView/MainViewController.swift
(Extension)
// MARK: - MKMapViewDelegate
extension MainViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let cityName = view.annotation?.title, let unwrappedCityName = cityName else { return }
navigateToWeather(for: unwrappedCityName)
mapView.deselectAnnotation(view.annotation, animated: true)
}
}
今天,我們為天氣 App 打造了一個美觀且直觀的入口。透過整合 MapKit
,使用者現在可以透過點擊地圖上的城市大頭針,輕鬆地查詢該地區的詳細天氣預報。
我們的天氣實戰 App 核心功能已經全部完成!從明天開始,我們將進回顧這 20 多天來的學習歷程,並展望在 AI 時代下,身為 iOS 開發者的我們,未來將會面臨哪些新的機會與挑戰。