iOS桌面小插件WidgetExtension

博客 分享
0 251
张三
张三 2022-03-06 12:56:11
悬赏:0 积分 收藏

iOS桌面小插件 Widget Extension

iOS桌面小插件 Widget Extension

  • 这个插件时iOS14以后才出现的,基于SwiftUI
  • 旧项目新建时可能一堆错误,其中一个时要把插件target 开发sdk版本设置为14.0以上

新建target

  • File - Target - Widget Extension

项目结构

  • @main 这里是主入口,这里可以设置小组件的 Provider以及 WidgetEntryView,以及长按后弹出框的 APP 信息设置。
  • Provider:控制器,这里可以用来做小组件的刷新操作
  • SimpleEntry: 这个是数据模型,Provider 里如果想更新数据到 WidgetEntryView,必须通过 SimpleEntry 来实现,当然命名随意了,但是这个必须继承 TimelineEntry。同时也可以新增参数,变量什么的,用来传递自己需要的数据类型。
  • WidgetEntryView: 这就是主视图了,在这里自定义页面用来显示在手机桌面。
import WidgetKitimport SwiftUIimport Intents// 控制器,类似Controller,这里可以用来做小组件的刷新操作struct Provider: IntentTimelineProvider {    func placeholder(in context: Context) -> SimpleEntry {        SimpleEntry(date: Date(), configuration: ConfigurationIntent())    }    func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {        let entry = SimpleEntry(date: Date(), configuration: configuration)        completion(entry)    }    func getTimeline(for configuration: ConfigurationIntent, 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, configuration: configuration)            entries.append(entry)        }        let timeline = Timeline(entries: entries, policy: .atEnd)        completion(timeline)    }}// 数据模型,数据显示在View上必须经过这里struct SimpleEntry: TimelineEntry {    let date: Date    let configuration: ConfigurationIntent}// View,小组件的界面struct WidgetExtensionEntryView : View {    var entry: Provider.Entry    var body: some View {        Text(entry.date, style: .time)            }}// 程序入口,初始化相关信息,如Provider,View等@mainstruct WidgetExtension: Widget {    let kind: String = "WidgetExtension"    var body: some WidgetConfiguration {        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in            WidgetExtensionEntryView(entry: entry)        }        .configurationDisplayName("小组件")        .description("This is an 测试一下 widget.")    }}// 自定义样式struct WidgetExtension_Previews: PreviewProvider {    static var previews: some View {                // 设置小组件尺寸 systemSmall systemMedium systemLarge        WidgetExtensionEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))            .previewContext(WidgetPreviewContext(family: .systemSmall))    }}

自定义UI

自定义小组件尺寸

// 自定义样式struct WidgetExtension_Previews: PreviewProvider {    static var previews: some View {                // 设置小组件尺寸 systemSmall systemMedium systemLarge        WidgetExtensionEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))            .previewContext(WidgetPreviewContext(family: .systemSmall))    }}

HStack、VStack、ZStack

  • HStack、VStack相当于UIStackView,H是水平方向,V是竖直方向。ZStack可以理解为相对于屏幕里外方向,也就是相当于以前superView和subView的方式。
// View,小组件的界面struct WidgetExtensionEntryView : View {    var entry: Provider.Entry    var body: some View {        // 深度布局,屏幕深度        ZStack(alignment: .center, content: {            // 背景图            Image("2").resizable().aspectRatio(contentMode: .fit)            //  水平            HStack(alignment: .center, spacing: 5, content: {                // 左侧图                Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)                // 垂直                VStack(alignment: .center, spacing: 5, content: {                    // 右侧文字                    Text("小组件1").foregroundColor(.blue)                    Text("小组件2").foregroundColor(.blue).lineLimit(2)                })            })        })    }}

传递数据

  • 通过widgetURL 和Link
  • 在主应用添加 URL Types
// View,小组件的界面struct WidgetExtensionEntryView : View {    var entry: Provider.Entry    var body: some View {        // 深度布局,屏幕深度        ZStack(alignment: .center, content: {            // 背景图            Image("2").resizable().aspectRatio(contentMode: .fit)            //  水平            HStack(alignment: .center, spacing: 5, content: {                // 左侧图                Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)                // 垂直                VStack(alignment: .center, spacing: 5, content: {                    // 右侧文字                    Text("小组件1").foregroundColor(.blue)                    Text("小组件2").foregroundColor(.blue).lineLimit(2)                })            })        }).widgetURL(URL(string: "widgetExtensionDemo://test1"))    }}
  • 接受数据

  • 只能用SceneDelegate来接受数据,AppDelegate不行。

  • SceneDelegate

  • SceneDelegate中相应事件

- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts{    NSLog(@"%s",__FUNCTION__);    UIOpenURLContext * context = URLContexts.allObjects.firstObject;    NSLog(@"%@", context.URL);}

适配不同尺寸小组件


// View,小组件的界面struct WidgetExtensionEntryView : View {    @Environment(\.widgetFamily) var family:WidgetFamily    var entry: Provider.Entry    var body: some View {        switch family {        case .systemSmall:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fill)                //  水平                HStack(alignment: .center, spacing: 5, content: {                    // 左侧图                    Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)                    // 垂直                    VStack(alignment: .center, spacing: 5, content: {                        // 右侧文字                        Text("小组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    })                })            }).widgetURL(URL(string: "widgetExtensionDemo://test1"))        case .systemMedium:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fill)                //  水平                HStack(alignment: .top, spacing: 5, content: {                    // 左侧图                    Image("1").resizable().aspectRatio(contentMode: .fit).frame(width: 200, height: 80, alignment: .leading).cornerRadius(10.0).foregroundColor(.blue)                    // 垂直                    VStack(alignment: .trailing, spacing: 5, content: {                        // 右侧文字                        Text("zh组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    }).foregroundColor(.gray)                })            }).widgetURL(URL(string: "widgetExtensionDemo://test2"))        case .systemLarge:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fill)                //  水平                HStack(alignment: .center, spacing: 5, content: {                    // 左侧图                    Image("1").aspectRatio(contentMode: .fit).cornerRadius(10.0).frame(width: 200, height: 100, alignment: .leading)                    // 垂直                    VStack(alignment: .center, spacing: 5, content: {                        // 右侧文字                        Text("小组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    })                }).foregroundColor(.blue)            }).widgetURL(URL(string: "widgetExtensionDemo://test3"))                    default:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fit)                //  水平                HStack(alignment: .center, spacing: 5, content: {                    // 左侧图                    Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)                    // 垂直                    VStack(alignment: .center, spacing: 5, content: {                        // 右侧文字                        Text("小组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    })                })            }).widgetURL(URL(string: "widgetExtensionDemo://test1"))        }    }}

更多小组件创建

  • 重写@main入口
// 更多小组件@mainstruct Widgets:WidgetBundle {    init() {            }    @WidgetBundleBuilder    var body: some Widget{ // 最多创建5次,也就是15个小组件        WidgetExtension()        CustomWidget()        CustomWidget()        CustomWidget()        CustomWidget()    }    }struct CustomWidget:Widget {    var kind:String="自定义组件"    var body: some WidgetConfiguration{        IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in            CustomEntryView(entry:entry)        }        .configurationDisplayName("自定义更多组件")        .description("ios14自定义更多小组件")    }    }// 自定义Uistruct CustomEntryView:View {    @Environment(\.widgetFamily) var family:WidgetFamily    var entry: Provider.Entry    @ViewBuilder    var body: some View {        switch family {        case .systemSmall:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fill)                //  水平                HStack(alignment: .center, spacing: 5, content: {                    // 左侧图                    Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)                    // 垂直                    VStack(alignment: .center, spacing: 5, content: {                        // 右侧文字                        Text("小组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    })                })            }).widgetURL(URL(string: "widgetExtensionDemo://test1"))        case .systemMedium:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fill)                //  水平                HStack(alignment: .top, spacing: 5, content: {                    // 左侧图                    Image("1").resizable().aspectRatio(contentMode: .fit).frame(width: 200, height: 80, alignment: .leading).cornerRadius(10.0).foregroundColor(.blue)                    // 垂直                    VStack(alignment: .trailing, spacing: 5, content: {                        // 右侧文字                        Text("zh组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    }).foregroundColor(.gray)                })            }).widgetURL(URL(string: "widgetExtensionDemo://test2"))        case .systemLarge:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fill)                //  水平                HStack(alignment: .center, spacing: 5, content: {                    // 左侧图                    Image("1").aspectRatio(contentMode: .fit).cornerRadius(10.0).frame(width: 200, height: 100, alignment: .leading)                    // 垂直                    VStack(alignment: .center, spacing: 5, content: {                        // 右侧文字                        Text("小组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    })                }).foregroundColor(.blue)            }).widgetURL(URL(string: "widgetExtensionDemo://test3"))                    default:            // 深度布局,屏幕深度            ZStack(alignment: .center, content: {                // 背景图                Image("2").resizable().aspectRatio(contentMode: .fit)                //  水平                HStack(alignment: .center, spacing: 5, content: {                    // 左侧图                    Image("1").frame(width: 80, height: 80, alignment: .center).aspectRatio(contentMode: .fit).cornerRadius(10.0)                    // 垂直                    VStack(alignment: .center, spacing: 5, content: {                        // 右侧文字                        Text("小组件1").foregroundColor(.blue)                        Text("小组件2").foregroundColor(.blue).lineLimit(2)                    })                })            }).widgetURL(URL(string: "widgetExtensionDemo://test1"))        }    }}

参考1
参考2

本文来自博客园,作者:struggle_time,转载请注明原文链接:https://www.cnblogs.com/songliquan/p/15971298.html

posted @ 2022-03-06 12:11 struggle_time 阅读(1) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员