iOS Widget开发1:创建一个Widget Extension

了解Widget

官方学习WidgetKit Demo
Widgets显示相关的、可浏览的内容,允许用户快速访问你的App以获取更多详细信息。你的App可以提供多种小组件,让用户专注于对他们最重要的信息。
向你的应用添加一个小组件需要少量的设置,以及一些关于你的用户界面的配置和风格的决定。小组件使用SwiftUI视图显示其内容。有关详细信息,请参见SwiftUI。

添加一个Widget到你的App

Widget Extension模板为创建小组件提供了一个起点。一个Widget Extension可以包含多种类型的小组件。例如,一个体育应用程序可能有一个小组件显示团队信息,另一个小组件显示比赛日程,这个例子一个Widget Extension包含了两个组件。建议在一个Widget Extension中包含你的App所有小组件。
添加步骤如下:

  1. 在Xcode中打开你的项目,然后选择 File > New > Target 。
  2. 从“Application Extension”组中,选择“Widget Extension”,然后单击“Next”。如下图:


    iOS Widget开发1:创建一个Widget Extension_第1张图片
    1.png
  3. 然后,给小组件命令,如下:


    iOS Widget开发1:创建一个Widget Extension_第2张图片
    2.png
  4. 如果小组件提供用户可配置的属性,请选中include configuration intent复选框。
  5. 点击Finish,会生成你们组件文件,如:AppWidget.swift、AppWidget.intentdefinition、Assets.xcassets、Info.plist 。如下图:


    iOS Widget开发1:创建一个Widget Extension_第3张图片
    3.png

然后你可以运行到iOS14的设备上看效果,会看到默认的小时钟组件。


IMG_0039.PNG

配置描述

widget extension模板提供了一个符合widget协议的初始widget实现。此小组件的body属性决定小组件是否具有用户可配置的属性。有两种配置:

  • StaticConfiguration:对于没有用户可配置属性的小组件。例如,显示一般市场信息的股市小组件,或显示趋势标题的新闻小组件。
  • IntentConfiguration:对于具有用户可配置属性的小组件。你可以使用SiriKit自定义内容来定义属性。例如,需要一个城市的邮政编码的天气小组件,或者需要跟踪号码的包裹跟踪小组件。
    要初始化配置,请提供以下信息:
  1. Kind: 标识小部件的字符串。这是一个你选择的标识符,应该描述小部件所代表的内容。
  2. Provider:一个符合TimelineProvider的对象,它生成一个时间轴,告诉WidgetKit何时呈现小组件。时间线包含您定义的自定义TimelineEntry类型。时间线条目标识您希望WidgetKit更新小组件内容的日期。包括小组件视图需要在自定义类型中呈现的属性。
  3. Placeholder View: WidgetKit第一次使用SwiftUI视图呈现小组件。占位符是小组件的通用表示,没有特定的配置或数据。
  4. Content Closure: 包含SwiftUI视图的闭包。WidgetKit调用它来呈现小组件的内容,从提供者传递一个TimelineEntry参数。
  5. Custom Intent: 定义用户可配置属性的自定义意图。有关添加自定义更多信息,请参阅 Making a Configurable Widget。
    例如下面代码显示IntentConfiguration类型的组件:
@main
struct AppWidget: Widget {
    private let kind: String = "AppWidget" //小组件的标识

    public var body: some WidgetConfiguration {
        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider(), placeholder: PlaceholderView()) { entry in
            AppWidgetEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget") //小组件的名称
        .description("This is an example widget.") //小组件的描述
        .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) //用户选择小部件、中版本或大版本的组件
    }
}

在上面代码中,小组件使用PlaceholderView()作为占位符视图,并在内容闭包中使用AppWidgetEntryView。占位符视图显示小组件的通用表示,让用户大致了解小部件显示的内容。不要在占位符视图中包含实际数据。例如,使用灰色框表示文本行,或使用灰色圆表示图像。
Provider为小组件生成一个时间轴,当每个时间轴条目的日期到达时,WidgetKit调用content闭包来更新显示小组件的内容。
注意这个小组件上@main属性的用法。此属性指示AppWidget是小组件扩展的入口点,表示该扩展包含一个小组件。

TimelineProvider

TimelineEntry为TimelineProvider提供数据信息来源,如下所示:

struct SimpleEntry: TimelineEntry {
    public let date: Date
    public let configuration: ConfigurationIntent
}

为了在窗口小组件库中显示小组件,WidgetKit要求提供者提供预览快照。你可以通过检查此 snapshot(for:with:completion:)方法中context的isPreview属性来判断小组件显示情况。当isPreview为true时,WidgetKit会在widget库中显示你的widget。对于有网络数据的话,你需要先提供一个默认数据显示快照。如下示例代码:

struct Provider: IntentTimelineProvider {
    public func snapshot(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (SimpleEntry) -> ()) {
        print(context.isPreview) //显示widget的显示情况
        let entry = SimpleEntry(date: Date(), configuration: configuration)
        completion(entry)
    }
}

WidgetKit框架要求我们用timeline(for:with:completion:)来控制刷新小组件的时间。

struct Provider: IntentTimelineProvider {
    public func snapshot(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (SimpleEntry) -> ()) {
        print(context.isPreview) //显示widget的显示情况
        let entry = SimpleEntry(date: Date(), configuration: configuration)
        completion(entry)
    }

    public func timeline(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (Timeline) -> ()) {
        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, configuration: configuration)
            entries.append(entry)
        }

        // Create the timeline with the entry and a reload policy with the date
        // for the next update.
        let timeline = Timeline(entries: entries, policy: .atEnd)
        // Call the completion to pass the timeline to WidgetKit.
        completion(timeline)
    }
}

小组件的显示内容

小组件UI使用SwiftUI来编写的,当用户从widget库中添加你的widget时,他们会从widget支持的组件中选择特定的组件(小型、中型或大型),widget的内容闭包必须能够呈现widget支持的每个组件。根据组件类型可以不同的实现,代码示例如下:

struct AppWidgetEntryView : View {
    @Environment(\.widgetFamily) var family: WidgetFamily
    var entry: Provider.Entry
    
    //view使用@ViewBuilder声明,因为它使用的view类型不同。
    @ViewBuilder
    var body: some View {
        switch family {
        case .systemSmall:
            Text(entry.date, style: .time)
        case .systemMedium:
            Text(entry.date, style: .time)
        case .systemLarge:
            Text(entry.date, style: .time)
        default:
            Text(entry.date, style: .time)
        }
    }
}

小组件显示只读信息,不支持交互元素,如滚动元素或开关。

在一个Widget Extension创建多个小组件

要支持多个小组件,需要声明一个符合WidgetBundle的结构,WidgetBundle在其body属性中将多个小组件组合在一起。在这个Widget bundle结构上添加@main属性,告诉WidgetKit你的扩展支持多个widget。代码示例:

@main
struct AppWidgets: WidgetBundle {
    @WidgetBundleBuilder
    var body: some Widget {
        AppWidget()
        //可以继续添加其它Widget
    }
}

你可能感兴趣的:(iOS Widget开发1:创建一个Widget Extension)