[Day 11] Geofencing — 實作地理圍欄功能

前言 在昨天的內容中,我們學會如何利用 Core Location 搭配 MapKit 即時取得並標記用戶的位置。今天,我們將實作地理圍欄(Geofencing)技術,它不僅能偵測使用者是否進入或離開特定區域,還能結合本地通知即時提醒,我們的 App 之後也會用到這個技術。 什麼是 Geofencing? Geofencing(地理圍欄)是一種以座標為中心、半徑為範疇的虛擬區域,透過 Core Location 框架自動監控使用者是否進入或離開該範圍,並能觸發事件通知,例如打卡、到站提醒、商圈推播都跟這個技術相關。 實作 介面設計 首先在 ContentView.swift 設計一個簡單的輸入區,供使用者自訂要監控的目標緯度與經度。 @State private var latitudeText: String = "" @State private var longitudeText: String = "" @State private var isMonitoring = false // 依據是否監控中,UI 隨之改變 // ... // MARK: - Geofencing 輸入介面 private var geofenceInputSection: some View { VStack(spacing: 12) { Text("設定地理圍欄") .font(.headline) HStack { TextField("緯度 (例: 25.033964)", text: $latitudeText) .keyboardType(.decimalPad) .textFieldStyle(.roundedBorder) TextField("經度 (例: 121.564468)", text: $longitudeText) .keyboardType(.decimalPad) .textFieldStyle(.roundedBorder) } HStack(spacing: 12) { Button(isMonitoring ? "正在監控中" : "建立地理圍欄") { startGeofencing() // 開始監控,待後面實作 } .disabled(isMonitoring || latitudeText.isEmpty || longitudeText.isEmpty) .buttonStyle(.borderedProminent) if isMonitoring { Button("停止監控") { stopGeofencing() // 停止監控,待後面實作 } .buttonStyle(.bordered) .foregroundColor(.red) } } if isMonitoring { Text("正在監控半徑100公尺的地理圍欄") .font(.caption) .foregroundColor(.secondary) } } .padding() .background(Color(.systemGray6)) .cornerRadius(12) } 為了讓 body 不要太肥,我們把這個輸入區域包在一個 geofenceInputSection computed property 裡,之後再只要像用變數一樣取用geofenceInputSection,直接把它放在 body 裡。 ...

September 24, 2025 · 4 分鐘

[Day 10] MapKit 基本應用

前言 在前一天我們已經學會了如何使用 Core Location 來取得用戶位置,而今天我們要進一步將位置資訊反映在地圖上。我們可以使用 MapKit 此一蘋果提供的地圖框架,此框架提供了顯⽰、導覽地圖,在地圖上加上標記,覆蓋物等功能。 今天簡單三個目標: 顯示地圖 鏡頭定位在「目前位置」 在目前位置放一個大頭針(Marker) 將地圖 view 加入畫面將位置資訊顯示在地圖上 我們可以延續昨天使用 Core Location 的專案,使用我們已經建立好的 LoactionManager 來取得位置,並且將位置顯示在地圖上。 首先引入 MapKit: import MapKit 在 MapKit 當中,我們找到這個初始化方法: @MainActor @preconcurrency public init<C>( position: Binding<MapCameraPosition>, bounds: MapCameraBounds? = nil, interactionModes: MapInteractionModes = .all, scope: Namespace.ID? = nil, @MapContentBuilder content: () -> C ) where Content == MapContentView<Never, C>, C : MapContent 說明一下幾個參數: position: Binding Binding<MapCameraPosition>,作用是透過雙向綁定,允許程式碼與地圖之間都能改變相機位置,常用值有: .automatic - 自動決定相機位置 .userLocation(fallback: .automatic) - 跟隨使用者位置 .region(MKCoordinateRegion(...)) - 指定特定區域與縮放程度 .camera(MapCamera(...)) - 3D 相機控制(高度、俯仰角等) bounds: MapCameraBounds? = nil ...

September 23, 2025 · 3 分鐘

[Day 9] Core Location 基礎

前言 Core Location 是 iOS 開發中用於處理地理位置相關功能的框架。今天的目標是了解如何使用 Core Location 來管理權限以及獲取用戶的當前位置。 使用 CLLocationManager 管理權限與更新位置 首先我們先在專案資料夾建立一個新的 .swift 檔 LoactionManager.swift。 可以在專案資料夾按快捷鍵 cmd + N,選擇 iOS -> Swift File。 我們的目的是建立一個獨立的管理者 LocationManager,它會處理向使用者請求定位權限、接收座標更新、並將這些資訊即時「發布」給 SwiftUI 介面,讓畫面可以根據最新的位置或權限狀態自動更新。 import CoreLocation class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { // .... } NSObject: 繼承自 NSObject。因為 Core Location 框架是基於早期 Objective-C 的設計模式。 ObservableObject: 遵循 ObservableObject 協定。這是一個來自 Combine 框架的宣告,表示這個物件可以被 SwiftUI 的 View 所「觀察」。一旦物件內被 @Published 標記的屬性發生改變,它會自動通知所有正在觀察它的 View 進行更新。 Combine 框架是用來在 Swift 中以聲明式方式處理資料流與非同步事件的工具,簡單說就是你不用自己到處寫通知、代理或 callback 處理資料或事件的一套工具。 CLLocationManagerDelegate: 必須遵循 CLLocationManagerDelegate 協定,LocationManager 才有能力處理「權限狀態改變」或「位置更新」等 delegate method。 接著我們需要: ...

September 22, 2025 · 3 分鐘

[Day 8] 讀取與解析 CSV 資料

前言 在 App 開發中,有時需要從專案內置的資源檔(如 CSV、JSON)讀取資料。CSV 是用逗點分隔值的檔案,我們可以將之轉化為 SwiftUI 列表。這項操作技能很實用,因為許多資料都是以這種簡單的格式儲存的,而我們這次要實作的 App 也會需要使用到這個功能,因此有必要先了解一下這個主題。 要完成這個任務,我們需要理解並掌握以下幾個核心概念: 讀取 App 內部檔案:學習如何使用 Bundle 來定位並讀取包含在我們 App 安裝包內的資源檔案,例如這次要用的 products.csv。 資料解析 (Parsing):取得原始的文字資料後,我們需要將其一行一行地解析,並將每個欄位轉換成我們定義好的 Product 資料結構,這個過程稱為資料解析。 動態列表 List:這個我們在昨天有使用過了,今天也會用到它。 Identifiable 協定:我們的資料模型(Product struct)必須遵循 Identifiable 協定,告訴 SwiftUI 如何唯一識別每一個項目。 那我們就開始吧。 Step 1:將 products.csv 加入 Xcode 專案 我們先將 products.csv 檔案拖曳到 Xcode 左側的專案導覽器中,並確保在跳出的視窗中勾選了 “Copy files to destination” 以及您的 App Target。 按下 Finish 後,可以看到檔案成功被加入到專案裡頭。 Step 2:定義產品資料模型 我們需要建立一個 struct 來對應 CSV 中的每一行資料。為了讓 SwiftUI 的 List 能識別每個項目,這個 struct 必須遵循 Identifiable 協定。 ...

September 21, 2025 · 4 分鐘

[Day 7] SwiftUI 列表與導航

台 7 線 15k SwiftUI 列表與導航 在 iOS 中,列表(List)和導航(Navigation)是兩個非常重要的基礎元件。前者用於展示大量資料,後者則為畫面切換機制。今天我們將探討這兩個重要的主題,並透過實作一個簡單的列表應用來理解相關概念。 List 元件與 ForEach 的基本使用 SwiftUI 的 List 元件使用方式如下: struct ContentView: View { ![alt text](image.png) var body: some View { List { ForEach(items, id: \.self) { item in Text(item) } } } } 在上面的例子中,我們使用了 ForEach 來遍歷陣列中的元素。 id 是什麼? id 參數用於為 ForEach 中的每個項目提供唯一的標識符。SwiftUI 透過這個標識符來識別和追蹤 list 中的每個視圖。當使用 id: \.self 時,表示直接使用被遍歷集合中的元素本身作為其唯一標識符。這種方式適用於集合中的元素本身是簡單且唯一的類型,例如 Int、String。 使用 \.self 作為 id 的前提是,你的資料集合 items 中的元素類型必須遵守 Hashable 協議。Hashable 能確保每個元素都可以被計算出一個唯一的雜湊值,從而讓 SwiftUI 能區分它們。 ...

September 20, 2025 · 2 分鐘