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用来判断当前组件的环境情况,即组件的尺寸信息,上面代码根据不同的尺寸渲染了不同格式的时间。


目录
相关文章
|
6天前
|
安全 Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的深度对比
在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文旨在通过数据驱动的分析方法,深入探讨这两大操作系统在开发环境、用户界面设计及市场表现等方面的差异。引用最新的行业报告和科研数据,结合技术专家的观点,本文将提供对开发者和市场分析师均有价值的洞见。
|
16天前
|
安全 IDE Android开发
探索Android与iOS开发的差异:平台特性与编程实践
【6月更文挑战第17天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据半壁江山。它们在用户群体、系统架构以及开发环境上的差异,为开发者带来了不同的挑战和机遇。本文深入探讨了这两个平台在技术实现、界面设计、性能优化等方面的主要区别,并提供了实用的开发建议,旨在帮助开发者更好地理解各自平台的特性,从而创造出更加优秀的移动应用。
|
19天前
|
安全 Android开发 iOS开发
探索Android与iOS开发的差异:平台特性与用户体验的对比分析
在移动应用开发的广阔天地中,Android和iOS两大阵营各据一方。本文将深入探讨这两个操作系统在开发环境、编程语言、用户界面设计及市场分布等方面的主要区别。通过比较分析,我们将揭示各自平台的特有优势,并讨论如何根据目标受众和业务需求选择适合的开发平台。
|
1天前
|
机器学习/深度学习 人工智能 文字识别
文本,文字扫描01,OCR文本识别技术展示,一个安卓App,一个简单的设计,文字识别可以应用于人工智能,机器学习,车牌识别,身份证识别,银行卡识别,PaddleOCR+SpringBoot+Andr
文本,文字扫描01,OCR文本识别技术展示,一个安卓App,一个简单的设计,文字识别可以应用于人工智能,机器学习,车牌识别,身份证识别,银行卡识别,PaddleOCR+SpringBoot+Andr
|
26天前
|
数据采集 JSON 算法
使用Python爬取华为市场APP应用进行分析
这个网站也是作者最近接触到的一个APP应用市场类网站。讲实话,还是蛮适合新手朋友去动手学习的。毕竟爬虫领域要想进步,还是需要多实战、多分析!该网站中的一些小细节也是能够锻炼分析能力的,也有反爬虫处理。甚至是下载APP的话在Web端是无法拿到APK下载的直链,需要去APP端接口数据获取
|
13天前
|
安全 Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的对比分析
移动应用开发的两大阵营——安卓与iOS,各自拥有独特的开发环境、用户群体和市场定位。本文将深入探讨这两个操作系统在应用开发过程中的主要差异,包括编程语言、开发工具、用户界面设计、性能优化、安全性考量以及发布流程等方面。通过比较分析,旨在为开发者提供跨平台开发的见解和策略,以优化应用性能和提升用户体验。
14 0
|
16天前
|
应用服务中间件 Linux 网络安全
PHP应用部署在App Service for Linux环境中,上传文件大于1MB时,遇见了413 Request Entity Too Large 错误的解决方法
在Azure App Service for Linux上部署的PHP应用遇到上传文件超过1MB时出现413 Request Entity Too Large错误的解决之法
|
2月前
|
安全 定位技术 网络安全
禁止应用在模拟器上运行的方案及app安全问题
禁止应用在模拟器上运行的方案及app安全问题
77 1
|
22天前
|
Android开发 Kotlin
kotlin开发安卓应用 如何修改app安装后的名称
在 Android 应用中,要修改安装后的显示名称,需更新 AndroidManifest.xml 文件中 application 标签的 android:label 属性。可直接在该属性内设置新名称,或在 res/values/strings.xml 文件中修改 app_name 并在 manifest 中引用。推荐使用 strings.xml 方式,以便支持多语言和集中管理。
|
2月前
|
安全 数据安全/隐私保护 iOS开发
探索iOS 14中的隐私保护新特性
【5月更文挑战第31天】随着数字时代的发展,用户隐私保护日益成为公众关注的焦点。苹果公司在最新的iOS 14更新中引入了多项隐私保护功能,旨在为用户提供更加透明和可控的个人信息管理体验。本文将深入探讨这些新特性,分析它们如何增强用户的隐私安全,并讨论这些变化对开发者和用户的潜在影响。