iOS14新特性探索之二:App Widget小组件应用(一)-阿里云开发者社区

开发者社区> 珲yy少> 正文

iOS14新特性探索之二:App Widget小组件应用(一)

简介: iOS14新特性探索之二:App Widget小组件应用
+关注继续查看

 iOS 14除了引入了亮眼的App Clips功能外。还有一个也非常惹争议的功能就是App Widget。App Widget可以理解为小组件,在非常早的Android版本中就有了Widget的概念,应用开发者可以为系统开发自己应用相契合的Widget来让用户更加方便的使用应用提供的功能。例如Android早期系统中非常常见的钟表时间组件、快捷设置组件等。用户可以将这些小组件根据自己的喜好放在屏幕的指定位置。从这点看,iOS 14提供的App Widget功能的确不能算是一种创新,最多算是一种增强。


       其实,iOS Widget的概念并非是iOS 14突然引入的,在iOS 10发布时,iOS系统就引入了Extension相关功能,其中有一种Extension叫做Today Extension,这就是iOS 14中Widget的前身。Today Extension允许开发者为负一屏开发快捷功能入口。关于Today Extension的应用,如下博客有详细的介绍:


iOS8新特性扩展(Extension)应用之一——Today扩展:https://my.oschina.net/u/2340880/blog/485533


iOS中Today扩展插件与宿主APP的交互:https://my.oschina.net/u/2340880/blog/711807


需要注意,在iOS 14中,Today Extension相关的接口都已经被废弃,我们需要使用新的WidgetKit框架提供的小组件接口开发Widget。在iOS 14上,Today Extension依然可以使用,但是其功能受限,只能在负一屏展示它,用户不能随意的将其放在指定屏的指定位置。


1. 关于App Widget


       Widget为应用程序提供了这样一种功能:其可以让用户在主屏幕上展示App中用户所关心的信息。例如一款天气软件,其可以附带一个Widget让用户在主屏幕就可查看今日的天气情况,例如股票相关的软件,用户将自己感兴趣的股票收藏,无需打开App,在主屏幕即可查到对应的股价信息。如下图所示,是系统提供的电池Widget展示在主屏幕上的示例:


image.png


一个App也可以提供多个Widget组件,用户可以选择将其最关心的放置在最重要的位置上,以便最方便的获取信息。对于同一种Widget组件,开发者也可以提供不同的尺寸或不同的布局,这可以提供给用户更多的选择以满足不同用户的偏好。


       为应用程序添加一个Widget组件并不复杂,但是有一点需要注意,小组件的UI部分只能够使用SwiftUI来开发,因此如果你要开发Widget组件,必须有一些Swift的基础并对SwiftUI有一定的了解。对于Swift与SwiftUI的相关内容,本篇博客就不再做过多赘述。


2. 创建App Widget


       与其他的Extension扩展类似,App Widget本身也是一种扩展,因此其只能依赖一个宿主App而存在,首先向已有的App中添加App Widget非常简单,为项目创建一个新的Target,选择其中的Widget Extension模板进行创建,如下图:


       image.png


创建完成后,Xcode会自动帮我们创建和配置的文件的工作都完成,默认的模板为我们创建了一个显示当前时间的组件,我们可以直接在真机上运行它(Bate版本的Xcode模拟器运行会有些异常),之后,我们就可以将这个显示时间的小组件放置在主屏幕的任意位置,并且,默认提供了3种尺寸供用户选择,如下图所示:


image.pngC


Xcode为我们创建的这个模板虽然简单,但是五脏俱全。Widget加载的入口是@main标记的结构体,代码如下:


@main

struct WidgetExt: Widget {

   private let kind: String = "WidgetExt"


   public var body: some WidgetConfiguration {

       StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in

           WidgetExtEntryView(entry: entry)

       }

       .configurationDisplayName("My Widget")

       .description("This is an example widget.")

   }

}

WidgetExt是我们为组件target项目设置的名字,模板自动使用这个名字帮我们生成了一个实现了Widget协议的结构体。结构体中实现了两个属性,其实Widget协议提供的核心只读属性只有一个body,将上面的代码改写如下也是一样的:


@main

struct WidgetExt: Widget {

   public var body: some WidgetConfiguration {

       StaticConfiguration(kind: "WidgetExt", provider: Provider(), placeholder: PlaceholderView()) { entry in

           WidgetExtEntryView(entry: entry)

       }

       .configurationDisplayName("My Widget")

       .description("This is an example widget.")

   }

}

上面代码的核心在于body只读属性的实现,其需要返回一个实现了WidgetConfiguration协议的示例。这个协议描述了组件的配置信息,StaticConfiguration是系统提供的组件配置结构体,其用来对静态类型的组件提供配置。StaticConfiguration完整的构造方法如下:


public init<Provider, PlaceholderContent>(

kind: String,

provider: Provider,

placeholder: PlaceholderContent,

content: @escaping (Provider.Entry) -> Content)

where Provider : TimelineProvider, PlaceholderContent : View

可以看到,上面构造方法中的Provider和PlaceholderContent实际上是两个泛型,我们后面再介绍。目前,我们先关注下构造方法需要传的几个参数。


kind:这个参数是一个字符号,我们可以任意提供,用来标识这个Widget组件。

provider:简单理解,这是一个数据提供对象,用来为小组件提供渲染数据,其必须实现TimelineProvider协议,即是基于时间线来驱动小组件的渲染。

placeholder:提供一个占位的视图,当小组件没有数据或者在锁屏状态时,会显示这个占位视图。

content:为小组件提供内容,是一个闭包,其中会把Provider的entry属性传入,因此小组件的视图渲染实际是由Provider驱动的。

   明白了上面几个参数的意义,开发小组件就非常轻松了。首先,需要创建一个合适的Provider来为小组件提供数据支持,以模板中的代码为例,如下:


struct Provider: TimelineProvider {

   public typealias Entry = SimpleEntry


   public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {

       let entry = SimpleEntry(date: Date())

       completion(entry)

   }


   public func timeline(with 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)

           entries.append(entry)

       }


       let timeline = Timeline(entries: entries, policy: .atEnd)

       completion(timeline)

   }

}


struct SimpleEntry: TimelineEntry {

   public let date: Date

}

如上代码所示,Provider结构体实现了TimelineProvider协议,这个协议中只定义了两个方法,分别是上面实现的snapshot方法和timeline方法。


       其中snapshop方法在小组件启动时会被调用一次,用来为小组件提供首屏渲染所需要的数据,其通常用来提供一些初始化的数据。调用完snapshot方法后,会调用timeline方法来定义要更新组件的时间线,这个方法的回调中需要传入一组Timeline对象,如上代码所示,其定义当前时刻开始,每隔一个小时进行一次刷新,将当前组件显示的时间刷新成最新的时刻,当最后一次刷新任务结束后,会再次调用timeline函数重新设置一组更新的时间线。关于时间线的详细介绍,后面会提及。


       有了Provider来对组件的更新提供驱动后,就是小组件页面的渲染了,在StaticConfiguration构造方法的闭包中,我们需要返回一个View作为小组件的内容,模板提供的示例代码如下:


struct WidgetExtEntryView : View {

   var entry: Provider.Entry


   var body: some View {

       Text(entry.date, style: .time)

   }

}

       在向主屏幕添加小组件时,用户可以选择不同尺寸的小组件进行添加,在小组件的渲染布局时,开发者也可以根据不同的环境尺寸配置不同的渲染策略,例如下面代码:


struct WidgetExtEntryView : View {

   @Environment(\.widgetFamily) var family: WidgetFamily

   

   var entry: Provider.Entry

   

   @ViewBuilder

   var body: some View {

       switch family {

       case .systemSmall: Text(entry.date, style: .time)

       case .systemMedium: Text(entry.date, style: .date)

       case .systemLarge: Text(entry.date, style: .relative)

       default: Text(entry.date, style: .time)

       }

   }

}

其中通过Enviroment用来判断当前组件的环境情况,即组件的尺寸信息,上面代码根据不同的尺寸渲染了不同格式的时间。


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
uni-app自定义组件模式开发注意事项
uni-app 自 1.8版本开始,新增支持自定义组件模式,该编译模式组件性能更高,支持更多的Vue语法。 请开发者尽快升级老版项目为自定义组件模式,老版的模板编译模式将不再维护。 Tips: uni-app 不同编译模式差异,参考:https://ask.
8755 0
Web系统下Office文档的处理 之OpenXml应用分类资料
本文对常见的处理参考和工具提供一个简要的参考。 对于实际的解决方案参考如下地址(有很多的分类知识) http://blogs.msdn.com/b/ericwhite/archive/2008/10/20/eric-white-s-blog-s-table-of-contents.
961 0
iOS新特性: iOS10.3教你如何动态更换APP图标?(后台切换) 韩俊强的博客
1、iOS 10.3 开放了更换 app 图标的 API,核心方法是下面这个: [[UIApplication sharedApplication] setAlternateIconName:nil completionHandler:^( }]; 这是官方文档,但是你还需要在 info.plist 里面填一些东西才能让它起作用,这部分官方注释内容在这里。
1356 0
Windows C++ 应用程序通用日志组件(组件及测试程序下载)
引言   众所周知,在调试、跟踪和执行应用程序的过程中,程序的日志能为这些工作提供大量有价值的运行信息。因此,程序的日志对应用程序的运行、维护至关重要。   在如何记录程序日志方面,通常有三种选择:   1、采用Log4CXX等公共开源日志组件:这类日志组件的特点是跨平台且功能比较强大,例如可以把日志发往另一台服务器或记录到数据库中等;另外,可配置性较高,可以通过配置文件或程序代码对日志进行很多个性化设置。
734 0
91%的商业App包含过时或废弃开源组件
Synopsys 公司发布了 2020 年开源安全和风险分析(OSSRA)报告,该报告由 Synopsys 网络安全研究中心(CyRC)制作,研究了由黑鸭审计服务团队进行的 1,250 多次商业代码库审计的结果。
604 0
+关注
594
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载