本文介绍: 小组件可以在主屏幕实现内容展示功能跳转系统会向小组件获取时间线,根据当前时间时间线上数据进行展示点击正在展示的视觉元素可以跳转到APP内,实现对应功能。小组件一个独立于 App 环境(即 App Extension),小组件生命周期/存储空间/运行进程都和 App 不同。​​​​​​​App Extension 可以自定义功能内容扩展应用程序之外,并在用户与其他应用程序系统交互时向用户提供。例如,您的应用可以在主屏幕显示为小部件

组件简述

组件可以在主屏幕实现内容展示和功能跳转
系统会向小组件获取时间线,根据当前时间时间线上数据进行展示。点击正在展示的视觉元素可以跳转到APP内,实现对应功能

小组件是一个独立于 App 环境(即 App Extension),小组件的生命周期/存储空间/运行进程都和 App 不同。

 ​​​​​​​

App Extension

App Extension 可以将自定义功能内容扩展应用程序之外,并在用户与其他应用程序或系统交互时向用户提供。例如,您的应用可以在主屏幕显示为小部件。也就是说小组件是一种 App Extension,小组件的开发工作基本都在 App Extension环境中。

App 和 App Extension有什么关系?

本质上是两个独立程序,你的主程序既不可以访问 App Extension代码,也不可以访问存储空间,这完完全全就是两个进程两个程序。App Extension 依赖你的 App 本体作为载体,如果将 App 卸载,那么 App Extension 也不会存在于系统中了。而且 App Extension生命周期大多数都作用于特定的领域,根据用户触发事件系统控制管理

相关限制

苹果基于上面的设计定位,同时也为了节省系统资源保证续航,对 Widget 的做了一些限制
支持动画,仅支持静态页面展示。
更新频率由系统通过机器学习动态分配
支持拖拽滚动复杂交互,不支持 Switch控件
用户点击 Widget 一定会跳转到 App。
支持三种不同大小样式

Widget小、大、中页面数据布局

public enum WidgetFamily : Int, RawRepresentable, CustomDebugStringConvertible, CustomStringConvertible {

    /// A small widget.
    case systemSmall

    /// A medium-sized widget.
    case systemMedium

    /// A large widget.
    case systemLarge
}

创建 Widget Extension 和配置文件​​​​​​​ 

​​​​​​​在 Xcode新增一个 Widget Extension

(路径如下:File-New-Target-iOS选项卡-Widget Extension)

 

需要配置小组件功能的,则不要忘记勾选 Include Configuration Intent

App Group数据通信

​​​​​​​App 和 App Extension是独立的两个Target,  是不能直接通讯的,所以需要共享信息时,需要使用 App Groups 来进行通讯。App Groups 有两种共享数据方式NSUserDefaultsNSFileManager

NSUserDefaults数据共享

 //  1. 写入共享数据
 if let myDefaults = UserDefaults(suiteName: "group.test.appgroup") {
    myDefaults.set("aaaaa", forKey: "key")
 }

 //  2. 读取共享数据
 if let myDefaults = UserDefaults(suiteName: "group.test.appgroup") {
    print("(myDefaults.object(forKey: "key"))")
 }

SwiftUI 构建组建

这里主要讲一下代理方法的作用,具体的视图布局和普通视图一样(widget只能用swiftUI来布局)

//
//  Widget1.swift
//  Widget1
//

import WidgetKit
import SwiftUI

// 时间线刷新策略控制
struct Provider: TimelineProvider {
    // 窗口首次展示的时候,展示默认数据
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date())
    }

    // 添加组件时的预览数据,在桌面滑动选择的时候展示数据
    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date())
        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 {
            // byAdding:刷新间隔枚举值可以选,按秒 分 小时...
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate)
            entries.append(entry)
        }

        // policy: TimelineReloadPolicy
        // atEnd 顺时针循环遍历
        // never 只循环一遍,循环完显示最后一个快照
        // after 指定刷新时间比如上面有5个快照,每秒更换数据源。TimeLine用after(10s), 那第二张就不会被看到
        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    // 默认带了一个日期参数
    let date: Date
}

struct Widget1EntryView : View {
    // 组件数据
    var entry: Provider.Entry

    // 这个 body 中就是自己需要实现的组件布局
    var body: some View {
        Text(entry.date, style: .time)
    }
}

// 小组件入口
@main
struct Widget1: Widget {
    // 小组件的唯一ID
    let kind: String = "Widget1"

    var body: some WidgetConfiguration {
        // 创建时不勾选 “Include Configuration Intent”,这里使用 StaticConfiguration
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            Widget1EntryView(entry: entry)  // 小组件UI
        }
        .supportedFamilies([.systemSmall, .systemLarge])  // 配置该组件支持的尺寸,如果不配置默认是大中小都支持
        .configurationDisplayName("组件标题")   // 在添加组件预览界面显示
        .description("组件描述")                 // 在添加组件预览界面显示
        .supportedFamilies([.systemSmall, .systemMedium])  //可以选择适配的小组件
    }
}

// 调试预览
struct Widget1_Previews: PreviewProvider {
    static var previews: some View {
        Widget1EntryView(entry: SimpleEntry(date: Date()))
            .previewContext(WidgetPreviewContext(family: .systemSmall))
    }
}

多个Widget


import WidgetKit
import SwiftUI

@main
struct testWidgetBundle: WidgetBundle {
    var body: some Widget {
        testWidget1()
        testWidget2()
        testWidget3()
    }
}

跳转至APP

widgetURL: 点击区域是Widget的所有区域,适合元素逻辑简单的小部件

Link: 通过Link修饰,允许让界面上不同元素产生点击响应
 

1.  widgetURL

//MARK: -小组件
//快捷启动small
struct ShortcutWidgetViewS : View {
    var date: Date
    var data: wShortcutData
    var body: some View {
        let info = data.infos[0]
        ZStack{
           Image(uiImage: data.bg).resizable()
        }.widgetURL(URL(string:"weixin://scanqrcode"))//这里是跳转微信扫一扫链接
    }
}

2.  Link

(Link可以选点击区域,不适用systemS mall)

struct SwiftUIShortcutCellL:View{
    var urlPath:String!//跳转的链接
    var body:some View{
        Link(destination: URL(string: urlPath)!) {
            ZStack(content: {
			//点击区域的UI
			Image(uiImage: data.bg)
            })  
        } 
    }
}

APP接收openURL
程序入口处,调用.onOpenURL方法,来接收传来的openURL

struct swiftUI_testApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    print(url)
                }
        }
    }
}

刷新次数限制

关于刷新策略,根据官方文档来看,Timeline的刷新策略是会延迟的,并不一定根据你设定的时间来。同时官方规定每个配置窗口部件每天都接收有限数量的刷新,影响widget刷新次数因素,主要有APP在前台和后台运行、APP的活跃度、widget的活跃度。

苹果因为提供了一个单独的方法调用来重新加载所有窗口部件
/// 控件的所有已配置部件重新加载时间线
/// 包含应用程序

WidgetCenter.shared.reloadAllTimelines()

可配置Widget

在 Xcode新增一个 SiriKit Intent Definition File

(路径如下:File-New File-iOS选项卡-SiriKit Intent Definition File) 

创建成功以后,就可以添加想配置的属性

代码获取配置属性

struct testWidgetEntryView : View {
    
    @Environment(.widgetFamily) var family: WidgetFamily
    
    var entry: Provider.Entry
    
    var body: some View {
        VStack {
            // 通过WidgetFamily的configuration属性,就可以拿到widget配置的属性
            Text(entry.configuration.title ?? "Title")
        }.widgetURL(URL(string: "SmallURL") )
    }
}

 运行结果
​​​​​​​

原文地址:https://blog.csdn.net/u010130947/article/details/128550517

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_15289.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注