熱烈歡迎,請直接點選!!!
進入博主App Store主頁,下載使用各個作品!!!
注:博主將堅持每月上線一個新app!!
支援原創,部落格園原文連結:https://www.cnblogs.com/strengthen/p/18362397
文末可以有demo下載。
首先檢視WWDC2024的官方影片:
WWDC2024將 App 控制元件擴充套件到系統級別:https://developer.apple.com/cn/videos/play/wwdc2024/10157/
There are two types of controls: buttons and toggles. Buttons perform discrete actions, which can include launching your app, while toggles change a piece of boolean state, like turning something on or off. 【翻譯為】:
控制元件有兩種型別:按鈕和切換按鈕。按鈕執行離散操作,包括啟動應用,而切換按鈕則更改布林狀態,例如開啟或關閉某些功能。
官方文件:Adding refinements and configuration to controls -https://developer.apple.com/documentation/WidgetKit/Adding-refinements-and-configuration-to-controls
詳細步驟:
1、點選連結:https://developer.apple.com/download/applications/ ,輸入Apple開發者賬號,下載Xcode 16.5 beta安裝包。
2、下載iOS 18.1 beta 2 模擬器執行時:https://developer.apple.com/download/all/,下載:【iOS 18.1 beta 2 Simulator Runtime】,也可以透過安裝Xcode16後,啟動Xcode16時,進行安裝iOS18模擬器執行時。
3、Xcode 16安裝模擬器執行時,請參考另一篇博文: https://www.cnblogs.com/strengthen/p/18348016
4、如果無法開啟Xcode16,請升級你的mac OS系統,升級為mac OS 14.5以上或者升級為mac OS 15 Beta::https://developer.apple.com/download/,下載macOS 15.1 beta 2。
5、新建Widget Extension:選擇Xcode16,螢幕左上角【File】-【New】-【Target】-【Widget Extension】
6、瞭解應用擴充套件的工作原理:https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html#//apple_ref/doc/uid/TP40014214-CH2-SW2
App Extension (應用擴充套件): 這是一個擴充套件應用功能的元件,可以在其他應用的上下文中提供特定的功能,如一個Share Extension可以讓使用者在Safari或Photos等其他應用內分享內容到你的應用。
Containing App (宿主應用): 這是指包含了上述擴充套件的主應用程式。也就是說,應用擴充套件是這個主應用的一部分,以擴充套件形式分發,並且與它同被打包到一個應用包中。
Host App (託管應用或主機應用): 這是指在當前上下文中呼叫或執行該應用擴充套件的應用。例如,如果使用者正在使用Safari並且觸發了一個Share Extension來分享一個網頁,那麼Safari就是這個應用擴充套件的host app。
(6.1)、app extension主要與其host app進行通訊,其通訊方式類似於事務處理:host app發出請求,app extension發出響應。
(6.2)、app extension與其宿主應用之間沒有直接通訊,當app extension執行時,宿主應用甚至不會執行。宿主應用和host app不通訊。
(6.3)、虛線表示app extension與其宿主應用之間可用的有限互動。app extension及其宿主應用,可以透過訪問私有定義的共享容器中的共享資料進行通訊。
(6.4)、在後臺,系統使用程序間通訊來確保host app和app extension能夠協同工作。在程式碼中無需考慮這種底層通訊機制,因為使用的是系統提供的更高階的 API。
7、 建立App Group共享儲存物件,用於app extension和app進行共享資料。
8、WWDC2024程式碼不夠詳細,直接上程式碼。
WidgetControlBundle.swift檔案:
// // WidgetControlBundle.swift // WidgetControl // // Created by iNFC on 2025/1/1. // import WidgetKit import SwiftUI // 標記:表示這是應用的入口點。它將這個 WidgetBundle 註冊為應用的一部分。 @main // Widget Bundle 是一種容器,允許你將多個控制元件組合在一起,以更好地組織和管理這些控制元件,並方便地在應用中啟用或禁用它們。 // WidgetControlBundle這是我們定義的 WidgetBundle 名稱。 struct WidgetControlBundle: WidgetBundle { // body屬性表示該 WidgetBundle 中包含的控制元件列表。你可以在這裡新增任意數量的控制元件。 var body: some Widget { // 型別一:Toggles:切換控制元件,切換開關狀態。 WidgetToggle() // 型別二:Buttons:執行離散操作,開啟App。 WidgetButton() } }
WidgetToggle.swift檔案:
// // WidgetToggle.swift // WidgetDemo // // Created by iNFC on 2025/1/1. // import Foundation import WidgetKit import SwiftUI import AppIntents import UIKit import Combine import Intents // 使用 ControlWidget 協議來定義一個基礎控制元件,定義了一個新的結構體 WidgetToggle,它實現了 ControlWidget 協議。 struct WidgetToggle: ControlWidget { // body屬性表示該 WidgetToggle 中包含的控制元件列表。 var body: some ControlWidgetConfiguration { // 這是一個靜態配置,用於定義控制元件的結構:外觀和行為。 StaticControlConfiguration( // 指定控制元件的唯一識別符號 kind: "com.apple.ControlWidgetToggle", // 使用定義的 TimerValueProvider 作為值提供者。 provider: TimerValueProvider() ) { isRunning in // :這是一個閉包,當獲取到定時器狀態時執行,其中 isRunning 表示當前的定時器狀態。透過這種方式,你可以使控制元件動態響應定時器狀態的變化,提供更好的使用者體驗。 // 定義了一個切換控制元件,使用 isOn 屬性表示當前的狀態。 isOn 屬性和操作意圖 action。 ControlWidgetToggle( "EasyShare", // 繫結到 TimerManager.shared.isRunning,表示定時器是否在執行。 isOn: isRunning, // 指定了一個 ToggleTimerIntent 操作,當使用者點選控制元件時觸發該操作。 action: ToggleTimerIntent() ){ isOn in // 這是一個閉包,用於根據 isOn 的值動態更新控制元件的圖示。
// 影像控制元件,用於顯示系統圖示。根據 isOn 的值選擇不同的圖示。 // Image(systemName: isOn ? "hourglass" : "hourglass.bottomhalf.filled") // 這是一個帶有文字和圖示的控制元件,用於顯示控制元件的狀態。 Label(isOn ? "Running" : "Stopped", // 根據 isOn 的值選擇顯示文字,執行時顯示“Running”,停止時顯示“Stopped”。 systemImage: isOn // 根據 isOn 的值選擇不同的圖示。 ? "hourglass.bottomhalf.filled" : "hourglass") // 這是一個方法,用於為控制元件新增操作提示,如“Start”或“Stop”。 .controlWidgetActionHint(isOn ? "Start" : "Stop") } // 設定控制元件的主題色為紫色,使其更具視覺吸引力。 .tint(.purple) } // 設定控制元件的顯示名稱為“iNFC”。 .displayName("iNFC") // 新增控制元件的描述。 .description("The most powerful NFC application") } } // 定義了一個新的結構體 TimerValueProvider,它實現了 ControlValueProvider 協議。 struct TimerValueProvider: ControlValueProvider { // 這是一個非同步函式,用於獲取當前的定時器狀態。 func currentValue() async throws -> Bool { // 獲取定時器的執行狀態。 return TimerManager.shared.fetchRunningState() } // 定義了一個預覽值,當無法獲取實際值時使用。 var previewValue: Bool { return false } } // 操作意圖(Intent),用於處理定時器的啟動和停止操作。定義了一個新的結構體,它實現了 SetValueIntent 和 LiveActivityIntent 協議。 struct ToggleTimerIntent: SetValueIntent, LiveActivityIntent { // 本地化字串資源 static var title: LocalizedStringResource = "WidgetToggle" // Parameter:這是一個引數註解,用於定義意圖中的引數。 // title:引數的標題。 @Parameter(title: "Toggle") // 一個布林值,表示定時器的執行狀態。 var value: Bool // 定義了執行意圖時的操作。 func perform() async throws -> some IntentResult { // 切換儲存開關狀態,呼叫 TimerManager 來設定定時器的執行狀態。 TimerManager.shared.setTimerRunning(value) // 儲存資料到Group App容器,傳遞給主應用 if let appGroupDefaults = UserDefaults(suiteName: "group.com.apple.iNFC") { if appGroupDefaults.bool(forKey: "widgetExtensionData") { appGroupDefaults.set(false, forKey: "widgetExtensionData") } else { appGroupDefaults.set(true, forKey: "widgetExtensionData") } } // 返回一個結果,表示意圖執行成功。 return .result() } }
TimerManager.swift檔案:
// // TimerManager.swift // WidgetDemo // // Created by iNFC on 2025/1/1. // import SwiftUI import Combine class TimerManager: ObservableObject { static let shared = TimerManager() @Published var isRunning = false private init() {} func setTimerRunning(_ value: Bool) { isRunning = value } func fetchRunningState() -> Bool { return isRunning } }
WidgetButton.swift檔案:
// // WidgetButton.swift // WidgetDemo // // Created by iNFC on 2025/1/1. // import Foundation import WidgetKit import SwiftUI import AppIntents import UIKit import Combine // 使用 ControlWidget 協議來定義一個基礎控制元件,定義了一個新的結構體 WidgetToggle,它實現了 ControlWidget 協議。 struct WidgetButton: ControlWidget { // body屬性表示該 WidgetButton 中包含的控制元件列表。 var body: some ControlWidgetConfiguration { // // 這是一個靜態配置,用於定義控制元件的結構:外觀和行為。 StaticControlConfiguration( kind: "com.apple.ControlWidgetButton" ) { // 定義了一個開啟容器App的控制元件, ControlWidgetButton(action: OpenContainerAction()) { Label("WidgetButton", systemImage: "paperplane") } actionLabel: { isActive in if isActive { Label("WidgetButton", systemImage: "hourglass") } } } .displayName("iNFC") .description("The most powerful NFC application") } } struct OpenContainerAction: AppIntent { // // 本地化字串資源 static let title: LocalizedStringResource = "WidgetButton" // 定義了執行意圖時的操作。 func perform() async throws -> some IntentResult & OpensIntent { // 儲存資料到Group App容器,傳遞給主應用 if let appGroupDefaults = UserDefaults(suiteName: "group.com.apple.iNFC") { if appGroupDefaults.bool(forKey: "widgetExtensionData") { appGroupDefaults.set(false, forKey: "widgetExtensionData") } else { appGroupDefaults.set(true, forKey: "widgetExtensionData") } } // 重要:開啟容器App的操作 return .result(opensIntent: OpenURLIntent(URL(string: "iNFC://")!)) } }
Github連結:demo下載