前言
前面创建了小组件项目,并做了低版本的兼容适配,有问题的小伙伴可以参考:
小组件入口
创建小组件项目后,可以看到入口文件内容如下:
在 SwiftUI 中,WidgetBundle 是用来定义和组织多个小组件(Widgets)的容器。它允许开发者将多个小组件组合在一个捆绑包中,使用户可以在 iOS、iPadOS、macOS 和 watchOS 上选择不同的小组件进行添加和使用。
WidgetBundle 主要功能:
-
组织多个小组件:WidgetBundle 用于将多个相关的小组件打包在一起,用户可以在添加小组件时看到捆绑包中的所有小组件选项。
-
结构和声明方式:需要实现 WidgetBundle 协议,并定义一个
body
属性,该属性包含所有需要捆绑的小组件。
小组件默认为 单组件 模式,创建多组件只需构建多个小组件视图并在 WidgetBundle 入口注册即可:
运行主项目就可以看到,此时会有两个小组件:
小组件代码
// MyWidget.swift
import WidgetKit
import SwiftUI
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), emoji: "😀")
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), emoji: "😀")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, emoji: "😀")
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let emoji: String
}
extension View {
// 背景
@ViewBuilder
func widgetBackground(_ backgroundView: some View) -> some View {
// 如果是 iOS 17,则使用 containerBackground
if #available(iOS 17.0, *) {
containerBackground(for: .widget) {
backgroundView
}
} else {
background(backgroundView)
}
}
}
struct MyWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
Text("Time:")
Text(entry.date, style: .time)
Text("Emoji:")
Text(entry.emoji)
}
.widgetBackground(Color.white)
}
}
struct MyWidget: Widget {
let kind: String = "MyWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
// 新增加
MyWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
.supportedFamilies([.systemSmall])
}
}
struct MyWidgetEntryView_Previews: PreviewProvider {
static var previews: some View {
MyWidgetEntryView(entry: SimpleEntry(date: Date(), emoji: "😁"))
.previewContext(WidgetPreviewContext(family: .systemSmall))
MyWidgetEntryView(entry: SimpleEntry(date: Date(), emoji: "😁"))
.previewContext(WidgetPreviewContext(family: .systemMedium))
}
}
打开 MyWidget.swift 文件,可以看到这个文件可以分为 5个 部分:
-
struct Provider:负责为小组件提供数据
-
struct SimpleEntry:小组件的数据模型
-
struct MyWidgetEntryView:小组件的视图
-
struct MyWidget:小组件的对象
-
struct MyWidgetEntryView_Previews:提供小组件在Xcode中的预览
Provider
Provider 继承了 TimelineProvider 协议,需要实现其中的三个方法:
func placeholder(in: ):在首次显示小组件,没有数据时使用占位
func getSnapshot(in: , completion: ):获取小组件的快照,例如在小组件库中预览时会调用
func getTimeline(in: , completion: ):这个方法来获取当前时间和未来时间的时间线小组件数据以更新小部件。说白点就是这个方法为小组件提供数据在什么时间显示什么内容。
SimpleEntry
SimpleEntry 继承实现了 TimelineEntry 协议,这个结构体是小组件Provider 的数据模型,用来定义数据的数据结构。
MyWidgetEntryView
MyWidgetEntryView 继承实现了 View 协议,是小组件的视图,决定了小组件的页面布局,它包含一个 SimpleEntry 的属性,为小组件提供了展示数据。
MyWidget
MyWidget 继承实现了 Widget 协议,这个就是小组件结构体,提供小组件的相关属性配置。
-
kind:小组件的唯一标识,可以随便填,以后当你的 App 有多个小组件,为了识别某个小组件时会用得上。
-
WidgetConfiguration:可以针对小组件做一些配置,比如名称、描述、支持的类型等。
MyWidgetEntryView_Previews
MyWidgetEntryView_Previews 继承实现了 PreviewProvider 协议,这个结构体的作用就是为小组件提供预览功能。
iOS17以下低版本使用这种方式:
iOS17及高版本可以使用这种方式:
友情链接
Swift 官方教程:
SwiftUI 官方教程:
developer.apple.com/tutorials/s…
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。