iOS 26 的小工具版面:一個 App Intent,多處現身
多年以來,小工具不過是一張快照。系統依照時間軸繪製您的視圖,使用者看一眼,任何點擊都只是開啟 App。那套模式已成過去。自 iOS 17 起,小工具就能搭載按鈕與開關,不必啟動任何東西便能執行程式碼,而它們執行的正是 App Intent1。控制中心的控制項是如此,即時動態裡的按鈕同樣如此。一旦看清這點,iOS 26 的小工具版面就不再像三個各自獨立的 API,而是合而為一:那是系統用來呈現您 App Intents 的一組場域,各有不同的版面空間,底層的管路卻完全相同。
這個視角的轉換,正是本文的核心。把 App Intent 學會一次,就能點亮主畫面、鎖定畫面、控制中心、動作按鈕、動態島,以及(同一個 intent)Siri 與 Apple Intelligence。若把它們寫成三套互不相干的功能,您付出四倍的工,換來的結果反而更差。
懶人包
- 互動式小工具(iOS 17 起): 在小工具視圖裡放入
Button(intent:)或Toggle(isOn:intent:);系統會在您的小工具擴充功能內執行 App Intent 的perform(),不開啟 App,接著重新載入時間軸1。 - 控制中心控制項(iOS 18 起): 以
ControlWidgetButton或ControlWidgetToggle構成的ControlWidget,背後同樣由 App Intents 驅動,會出現在控制中心、鎖定畫面與動作按鈕上2。 - 即時動態(ActivityKit):
ActivityAttributes搭配ActivityConfiguration,驅動鎖定畫面與動態島;由伺服器主導的更新透過推播送達,而其中的按鈕同樣是 App Intents3。 - iOS 26 新增的是呈現方式,而非模型本身: 小工具採用了 Liquid Glass 的呈現風格與強調色彩的繪製模式,但「App Intents 即版面」的架構,自 iOS 18 以來並未改變4。
- 串起這一切的事實是:小工具按鈕、控制項、Siri 語句,乃至 Apple Intelligence 的操作,全都可以是同一個 App Intent。能力只需設計一次。
互動式小工具:不開啟 App 的按鈕
這套機制不大,但值得精確說清楚。您在小工具的視圖裡加入 SwiftUI 的 Button 或 Toggle,但傳入的不是動作閉包,而是一個 App Intent1:
struct WaterWidgetView: View {
let logged: Int
var body: some View {
VStack {
Text("\(logged) glasses")
Button(intent: LogWaterIntent()) {
Label("Add", systemImage: "plus")
}
}
}
}
當使用者點擊時,系統會在您的小工具擴充功能內執行 LogWaterIntent.perform()。App 不會啟動。intent 完成它的工作(寫入共享儲存區、更新計數),小工具的時間軸隨即重新載入,呈現結果。
Toggle 透過 SetValueIntent 以同樣的方式運作,它會接收使用者剛剛設定的新開關值:
struct ToggleReminderIntent: SetValueIntent {
static let title: LocalizedStringResource = "Toggle Reminder"
@Parameter(title: "Enabled")
var value: Bool
func perform() async throws -> some IntentResult {
ReminderStore.shared.enabled = value
return .result()
}
}
小工具綁定 Toggle(isOn: store.enabled, intent: ToggleReminderIntent()),系統將 value 設為新狀態、執行 perform(),然後重新載入。形狀與按鈕如出一轍,只多了一個參數1。
有兩項限制界定了您在此能做什麼,而兩者都很容易違反。其一,perform() 在受限的小工具擴充功能環境中執行,執行預算相當吃緊;它適合快速的狀態變更,而非網路上傳或繁重運算。其二,小工具反映的是狀態,而非任意動畫 UI。您改變資料,時間軸便重新繪製;您無法執行自訂的轉場。把互動設計成「翻轉一個值,顯示新的值」,它就能順暢運作。一旦想要更多,系統便會與您作對。
控制中心控制項:同一個 intent,換個房間
iOS 18 把控制中心、鎖定畫面與動作按鈕開放給第三方控制項,其 API 刻意設計得與小工具如出一轍2。控制項是一個 ControlWidget,其主體是接上 App Intent 的 ControlWidgetButton 或 ControlWidgetToggle:
struct LogWaterControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: "com.example.logWater") {
ControlWidgetButton(action: LogWaterIntent()) {
Label("Log Water", systemImage: "drop.fill")
}
}
}
}
如果 LogWaterIntent 已經驅動您的互動式小工具,這個控制項幾乎不費吹灰之力:同一個 intent,換個外殼。這正是架構帶來的回報。控制項不是這項功能的第二套實作;它只是您早已寫好的那套,多了一個掛載點。開關控制項使用 SetValueIntent,與小工具的開關完全相同,因此「靜音」、「開始工作階段」或「切換深色模式」這類只設計一次的能力,無需新增任何邏輯,就會出現在控制中心、鎖定畫面與動作按鈕上2。
即時動態:會自己更新的場域
即時動態是第三個場域,也是動件最多的一個。ActivityAttributes 型別定義靜態與動態內容,ActivityConfiguration 繪製鎖定畫面的呈現與動態島的各個區域,ActivityKit 則負責啟動、更新與結束活動3。即時動態裡的按鈕,再次地,也是 App Intents,因此動態島裡的「暫停」或「下一個」控制項,執行的是與小工具按鈕同一類的 intent。
讓即時動態自成一門學問的,是它的更新路徑。本地更新的計時器很單純。但若活動是由伺服器端發生的事件所驅動(外送進度移動、比賽分數變動),更新就會透過推播進行:您註冊 pushTokenUpdates、從後端送出 ActivityKit 推播酬載,系統便會在您的 App 完全沒有執行的情況下,更新鎖定畫面與動態島3。這股力量確實強大,卻也確實容易出錯,因為此時您活動的正確性,取決於一份伺服器契約、推播的可靠度,以及一套過期日期策略,而不再只是本地程式碼。
iOS 26 究竟改了什麼
iOS 26 小工具的重頭戲是呈現,而非架構。小工具採用 Liquid Glass 材質,並獲得強調色彩的繪製模式,因此能契合系統全新的設計語言,並在作業系統會替內容重新上色的情境中正確著色4。這對小工具在主畫面、鎖定畫面與 StandBy 之間的外觀至關重要,值得專門做一次設計檢視。但它並未改變互動的運作方式。如果您已在 iOS 18 上打造了互動式小工具與控制項,iOS 26 帶來的是視覺翻新,而非重寫。對於聲稱 iOS 26「引入了」互動式小工具的說法,請抱持懷疑;互動性早在 iOS 17 就已推出,控制項在 iOS 18,而 iOS 26 只是讓它們看起來像系統其餘部分而已。
不過,Liquid Glass 在設計上的影響確實存在。小工具是系統覆疊在桌布上、再透過玻璃外殼折射的內容,因此與在 App 中運用 Liquid Glass 相同的那套規則同樣適用:尊重功能層與內容層的分層,別用玻璃讀不出來的繁重自訂背景去與材質對抗。
把架構說清楚
這裡有個值得記住的模型。一個 App Intent 是一項具名、具型別、有描述的能力。iOS 給您愈來愈多的場域,可以把這項能力掛載上去:
- 小工具內的
Button或Toggle。 - 出現在控制中心、鎖定畫面與動作按鈕上的
ControlWidget。 - 即時動態及其動態島裡的按鈕。
- 一句 Siri 語句與一個捷徑操作。
- 一項 Apple Intelligence 能代使用者執行的操作5。
以上每一項,都是帶有 perform() 方法的同一個 AppIntent 型別。功夫全在於把那項能力設計好:清晰的名稱、具型別的參數、快速且具冪等性的 perform(),以及合理的結果。把這件事做好一次,各個場域便都成了外殼。這正是我先前談 App Intents 是通往您 App 的真正 API 時所主張的論點,也是談 iOS App 如今所暴露的三個場域 時的論點:小工具版面不是一項您要打造的功能,而是您的能力一旦存在,就會現身的地方。
何時不必費這個心
小工具版面回報的,是那些真有值得一瞥的狀態與快速操作的 App:追蹤器、計時器、開關、正在播放的控制項。它並非回報一切。
- 沒有值得一瞥的狀態,就別做小工具。 如果您的 App 不開啟就沒什麼值得展示的,那小工具只是裝飾,徒增維護成本與一個額外的擴充功能目標。
- 繁重或緩慢的操作,不屬於這裡的
perform()。 小工具與控制項的執行環境受到限制。如果操作需要實打實的時間或運算,按鈕就該開啟 App 到一個備妥的狀態,而不是假裝在擴充功能裡把事做完。 - 替不「即時」的東西做即時動態。 即時動態是為有時限、正在變動的事件而生。把它當成常駐的狀態徽章,是對這個場域的誤讀,也是讓自己在系統的活動預算黑名單上現身的捷徑。
真正的本事不在於學會三個 API,而在於設計出值得掛載的 App Intents,然後把它們掛在使用者早已身處之地:他們會查看的主畫面、他們會滑去的控制中心、早已在向他們顯示某些東西的動態島。把能力用品味打造好一次,iOS 便會把這些場域免費奉上。
-
Apple Developer,“Adding interactivity to widgets and Live Activities”。互動式小工具(iOS 17 起)使用以
AppIntent(開關則用SetValueIntent)為後盾的 SwiftUIButton(intent:)與Toggle(isOn:intent:);intent 的perform()在小工具擴充功能內執行而不啟動 App,時間軸隨後重新載入以反映結果。 ↩↩↩↩ -
Apple Developer,“Creating controls to perform actions across the system” 以及
ControlWidget協定。控制項(iOS 18 起)由接上 App Intents 的ControlWidgetButton與ControlWidgetToggle構成,並出現在控制中心、鎖定畫面與動作按鈕上。 ↩↩↩ -
Apple Developer,“ActivityKit” 以及 “Displaying live data with Live Activities”。
ActivityAttributes與ActivityConfiguration定義並繪製鎖定畫面與動態島;ActivityKit 負責啟動、更新與結束活動,而由伺服器主導的更新使用搭配pushTokenUpdates的推播。 ↩↩↩ -
iOS 26 小工具繪製:小工具採用 Liquid Glass 材質與強調色彩的繪製模式,透過
WidgetRenderingMode與\.widgetRenderingMode環境值控制。互動模型(小工具與控制項中的 App Intents)自 iOS 17(小工具)與 iOS 18(控制項)以來並未改變。Apple,“WWDC 2025: the new software design” 以及 WidgetKit 文件。 ↩↩ -
Apple Developer,“App Intents”。同一個
AppIntent型別,正是系統透過 Siri、捷徑、Spotlight、小工具與控制項場域,以及 Apple Intelligence 所暴露的單元。作者對跨場域模型的分析:App Intents 是 Apple 通往您 App 的全新 API、App Intents 2 與 iOS 26 的新增功能,以及 iOS App 的三個場域。 ↩