美文网首页iOS进阶+实战
iOS开发之WidgetKit

iOS开发之WidgetKit

作者: YungFan | 来源:发表于2020-08-14 11:27 被阅读0次

    iOS 14 Apple 推出了 WidgetKit,Widget 就像一个迷你版的 App,可以快速访问它所提供的信息—比如天气、日历事件、笔记等。Widget 还可以充当“快捷方式”,点击它会立即跳转到 App 的指定位置。

    介绍

    • WidgetKit 通过在 iOS 主屏幕或 macOS 通知中心放置小部件,让用户可以随时访问 App 中的内容。Widget 可以保持更新,从而让用户获得最新信息。当需要更多细节时,Widget 会直接带到 App 中的适当位置。
    • Widget 有三种不同的尺寸(小号、中号和大号),可以对 Widget 进行个性化定制。
    • 要实现一个 Widget,需要给应用添加一个 Widget 扩展并只能使用 SwiftUI 来实现 Widget 的内容。

    App实现

    Widget 寄宿于 App,所以首先必须将 App 功能实现。

    添加Widget

    1. 点击项目,选择File > New > Target
    2. Application Extension中,选择Widget Extension,然后点击Next
    3. 输入扩展名的名称。
    4. 单击Finish
    5. 此时会生成一个新文件夹,包含以下内容
      • 扩展名.swift
      • 扩展名.intentdefinition
      • Assets.xcassets
      • Info.plist

    数据共享

    可以通过网络和本地数据两种方式进行数据的共享。本地数据共享可以通过
    App Groups,它是 iOS 8 之后推出的在 App 之间共享数据的方式,只需要简单的配置就可以实现数据的共享。

    配置

    1. App 在Signing&Capabilities中打开App Groups,内容一般为group.Bundle Identifier
    2. Widget 必须在Signing&Capabilities中打开App Groups,内容与 App 保持一致。
    App Groups

    如果文件需要共享,可以选中 App 中需要共享给 Widget 的文件,然后勾选 Widget 的 Target。

    实现

    配置完成以后,可以通过UserDefaultsFileManager来实现 App 与 Widget 的数据共享,这里以UserDefaults为例,因为 SwiftUI 提供了@AppStorage来简化操作。

    • App
    // 包含App Groups的UserDefaults
    @AppStorage("contact", store: UserDefaults(suiteName: "group.cn.abc.yf.SwiftUI-Widget"))
    
    // 然后在后面保存数据
    
    • Widget
    @AppStorage("contact", store: UserDefaults(suiteName: "group.cn.abc.yf.SwiftUI-Widget"))
    
    // 然后在后面取出数据
    

    编写Widget

    1. 原理:开发者通过 SwiftUI 构建 Views,定义Timelines为 Views 提供对应时间所需的数据,当数据变化时,通过reload更新数据。TimelineProvider提供一组TimelineEntryReloadPolicy,用来后续刷新页面。
    2. 实现 Widget 的代码相对比较模版,可以从 Widget 的入口开始,缺什么补什么。

    入口

    @main
    struct UserWidget: Widget {
        private let kind: String = "UserWidget"
        
        public var body: some WidgetConfiguration {
        
        }
    }
    
    • kind:字符串,唯一标识 Widget。
    • WidgetConfiguration:有两类配置,分别为
      • StaticConfiguration : 可以在不需要用户任何输入的情况下自行解析,可以在 Widget 的 App 中获取相关数据并发送给 Widget。
      • IntentConfiguration:依赖于 App 的 Siri Intent,会自动接收这些 Intent 并用于更新 Widget,用于构建动态 Widget。
    • .supportedFamilies:支持不同尺寸,示意图如下。
    三种尺寸

    内容

    不论是哪种配置,都需要提供以下内容。

    Entry

    渲染 Widget 所需的数据模型,需要遵守TimelineEntry协议。

    struct Model: TimelineEntry {
        public let date: Date
        // 模型结构体
    }
    

    Provider

    遵守TimelineProvider协议,告诉 WidgetKit 何时渲染与刷新 Widget。需要实现以下两个方法:

    struct Provider: TimelineProvider {
        
        // 编辑屏幕在左上角选择添加Widget、第一次展示时会调用该方法
        func snapshot(with context: Context, completion: @escaping (TimelineEntry) -> Void) {
        }
    
        // 进行数据的预处理(网络,文件等),转化成Entry
        // 最后一定要调用 completion,进而刷新Widget
        func timeline(with context: Context, completion: @escaping (Timeline<TimelineEntry>) -> Void) {
        }
    }
    
    1. Timeline 的构造函数里有一个 policy 参数,意思是在什么时候尝试丢弃当前时间线并获取一个新的时间线,可以选择.never,.atEnd 或 .after
    2. 如果需要强制刷新 Widget,可以在 App 中使用 WidgetCenter 来重新加载所有时间线:WidgetCenter.shared.reloadAllTimelines()

    PlaceholderView

    占位视图,是一个标准的 SwiftUI View,当第一次展示或者发生错误时都会展示该 View。

    struct PlaceholderView : View {
        
        var body: some View {
            Text("Loading...")
        }
    }
    

    EntryView

    屏幕上 Widget 显示的内容,需要使用 SwiftUI 构建,可以针对不同尺寸的 Widget 设置不同的 View。

    struct EntryView: View {
        
        var entry: Provider.Entry // 数据模型
        
        @Environment(\.widgetFamily) var family // 尺寸环境变量
    
        @ViewBuilder
        var body: some View {
            switch family {
            case .systemSmall:
                // 小尺寸
            case .systemMedium:
                // 中尺寸
            default:
                // 大尺寸
            }
        }
    }
    

    运行

    • 先运行 App
    • 再运行 Widget

    交互

    只能点击,点击会打开 App。也可以通过.widgetURL(myDeeplink)方法配置当 Widget 被点击时触发哪个 Deep Linking,也可以通过使用链接使 Widget 的不同部分触发不同的 Deep Linking。

    var body: some View {
        VStack {
            Link(destination: homeDeepLink) {
                Text("主页")
            }
            Link(destination: settingsDeepLink) {
                Text("设置")
            }
        }.widgetURL(homeDeeplink)
    }
    

    源代码

    Widget案例

    相关文章

      网友评论

        本文标题:iOS开发之WidgetKit

        本文链接:https://www.haomeiwen.com/subject/lacgdktx.html