一、小组件开发概念介绍
小组件是App Extension
的一种,前身为iOS8推出的Today Extension,
只能添加到负一屏。
iOS14推出Widget Extension
取代Today Extension
。 相比Today Extension
,Widget Extension
可以添加到桌面,并且支持了更多的尺寸,可以承载更多内容。
小组件开发依赖的主要官方框架是WidgetKit
,界面开发需要使用SwiftUI
,自定义配置、点击交互等和SiriKit
的机制相同。
SwiftUI
是苹果2019年推出的构建界面的声明式框架,相对UIKit
具有更高的开发效率和更好的可读性等优点。
二、小组件的创建
1.小组件的创建方式
1、在 Xcode 中打开 App 项目,并选取"File > New > Target" ,选择 "Widget Extension"
2.点击next
如果都不勾选系统默认创建入口文件(myExtensionWidgetBundle.swift)和一个widget(myExtensionWidget.swift)
可以自己创建多个小组件添加到myExtensionWidgetBundle.swift中创建多个小组件。
有三个可选项:
- Include Live Activity(灵动岛、实时活动相关)
- Include Control
- Include Configuration App Intent
2.小组件的入口文件:
使用 @main
属性指定小组件扩展的单个入口点。为支持多个小组件,需要声明一个符合 WidgetBundle 的结构,该结构会在其 body
属性中将多个小组件组合在一起。在这个 widget-bundle 结构上添加 @main
属性,以告诉 WidgetKit 你的扩展支持多个小组件。
import WidgetKit
import SwiftUI
@main
struct MyWidgetBundle: WidgetBundle {
var body: some Widget {
MyWidget()
MyWidgetControl()
MyWidgetLiveActivity()
}
}
注意:@WidgetBundleBuilder
的buildBlock
方法最多支持传入10个遵守Widget
协议的参数,因此一个WidgetBundle
最多可以支持放10个Widget。但可以
通过嵌套方式实现多个
@main
struct MyWidgetBundle: WidgetBundle {
var body: some Widget {
MyChildWidgetBundle().body
// 最多可以放10个`MyChildWidgetBundle().body`
}
}
struct MyChildWidgetBundle: WidgetBundle {
var body: some Widget {
MyGrandsonWidgetBundle().body
// 最多可以放10个`MyGrandsonWidgetBundle().body`
}
}
struct MyGrandsonWidgetBundle: WidgetBundle {
var body: some Widget {
MyWidget()
// 最多可以放10个`MyWidget()`
}
}
3.小组件的尺寸
目前小组件支持设置基础的三个尺寸,即:小、中、大。
struct MyWidget: Widget {
let kind: String = "MyWidget" // 唯一标识
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
MyWidgetEntryView(entry: entry)
}
.configurationDisplayName("这是小组件的名称")
.description("这是小组件的描述.")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge,.systemExtraLarge]) // 支持小、中、大尺寸,超大
}
}
注意:systemExtraLarge是ios15之后才会支持。
从 iOS 16 开始,支持 accessoryCircular
、 accessoryRectangular
和 accessoryInline
类型,这几种小部件可以用在手表中,也可以出现在 iOS 的锁定屏幕上。
在 Widget View 中可以从环境变量(Environment)中获取当前的小组件类型,获取之后以做不同的 UI 适配:
// 环境变量获取当前的小组件类型
@Environment(\.widgetFamily) var family: WidgetFamily
Include Live Activity
如果勾选Include Live Activity,系统会默认给我们创建一个myExtensionWidgetLiveActivity.swift文件
import ActivityKit
import WidgetKit
import SwiftUI
struct myExtensionWidgetAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// Dynamic stateful properties about your activity go here!
var emoji: String
}
// Fixed non-changing properties about your activity go here!
var name: String
}
struct myExtensionWidgetLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: myExtensionWidget2Attributes.self) { context in
// Lock screen/banner UI goes here
VStack {
Text("Hello \(context.state.emoji)")
}
.activityBackgroundTint(Color.cyan)
.activitySystemActionForegroundColor(Color.black)
} dynamicIsland: { context in
DynamicIsland {
// Expanded UI goes here. Compose the expanded UI through
// various regions, like leading/trailing/center/bottom
DynamicIslandExpandedRegion(.leading) {
Text("Leading")
}
DynamicIslandExpandedRegion(.trailing) {
Text("Trailing")
}
DynamicIslandExpandedRegion(.bottom) {
Text("Bottom \(context.state.emoji)")
// more content
}
} compactLeading: {
Text("L")
} compactTrailing: {
Text("T \(context.state.emoji)")
} minimal: {
Text(context.state.emoji)
}
.widgetURL(URL(string: "http://www.apple.com"))
.keylineTint(Color.red)
}
}
}
extension myExtensionWidgetAttributes {
fileprivate static var preview: myE