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


目录
相关文章
|
28天前
|
安全 Android开发 iOS开发
安卓与iOS的较量:技术特性与用户体验的深度解析
在移动操作系统的战场上,安卓和iOS一直占据着主导地位。本文将深入探讨这两大平台的核心技术特性,以及它们如何影响用户的体验。我们将从系统架构、应用生态、安全性能和创新功能四个方面进行比较,帮助读者更好地理解这两个系统的异同。
50 3
|
4天前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
|
9天前
|
监控 Android开发 iOS开发
深入探索安卓与iOS的系统架构差异:理解两大移动平台的技术根基在移动技术日新月异的今天,安卓和iOS作为市场上最为流行的两个操作系统,各自拥有独特的技术特性和庞大的用户基础。本文将深入探讨这两个平台的系统架构差异,揭示它们如何支撑起各自的生态系统,并影响着全球数亿用户的使用体验。
本文通过对比分析安卓和iOS的系统架构,揭示了这两个平台在设计理念、安全性、用户体验和技术生态上的根本区别。不同于常规的技术综述,本文以深入浅出的方式,带领读者理解这些差异是如何影响应用开发、用户选择和市场趋势的。通过梳理历史脉络和未来展望,本文旨在为开发者、用户以及行业分析师提供有价值的见解,帮助大家更好地把握移动技术发展的脉络。
|
9天前
|
存储 IDE 开发工具
移动应用开发之旅:打造你的首个iOS应用
【9月更文挑战第23天】在数字化浪潮中,移动应用已成为连接用户与数字世界的关键桥梁。本文将带领读者踏上开发属于自己的第一个iOS移动应用的旅程,从理解移动操作系统的核心概念出发,逐步深入到实际的应用构建过程中。通过简洁明了的语言和具体的代码示例,我们将一起探索如何在苹果的iOS平台上实现一个简单的“待办事项列表”应用,让读者不仅能够学习到编程知识,还能体会到将想法转化为现实产品的成就感。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供一个实用的指南,帮助你迈出成为移动应用开发者的第一步。
|
22天前
|
开发框架 Android开发 iOS开发
探索安卓与iOS开发的差异:构建未来应用的指南
在移动应用开发的广阔天地中,安卓与iOS两大平台各占半壁江山。本文将深入浅出地对比这两大操作系统的开发环境、工具和用户体验设计,揭示它们在编程语言、开发工具以及市场定位上的根本差异。我们将从开发者的视角出发,逐步剖析如何根据项目需求和目标受众选择适合的平台,同时探讨跨平台开发框架的利与弊,为那些立志于打造下一个热门应用的开发者提供一份实用的指南。
47 5
|
28天前
|
Swift iOS开发 UED
揭秘一款iOS应用中令人惊叹的自定义动画效果,带你领略编程艺术的魅力所在!
【9月更文挑战第5天】本文通过具体案例介绍如何在iOS应用中使用Swift与UIKit实现自定义按钮动画,当用户点击按钮时,按钮将从圆形变为椭圆形并从蓝色渐变到绿色,释放后恢复原状。文中详细展示了代码实现过程及动画平滑过渡的技巧,帮助读者提升应用的视觉体验与特色。
39 11
|
27天前
|
安全 Android开发 iOS开发
安卓与iOS的较量:技术特性与用户体验的深度剖析
在移动操作系统的战场上,安卓和iOS一直是两个重量级选手。本文将深入探讨两者的技术架构、安全性、应用生态以及用户体验等方面的差异,并尝试从用户和开发者的角度出发,分析这两个系统的优势与不足。通过比较,我们不仅能更好地理解各自的特点,还能洞察未来移动技术的发展趋势。
|
1月前
|
API iOS开发
探索iOS开发:打造你的第一个天气应用
【8月更文挑战第31天】 在这篇文章中,我们将一起潜入iOS开发的海洋,从初学者的角度出发,一步步构建我们自己的天气应用。通过实际的项目实践,你将学习到如何获取网络数据、如何在界面上展示这些数据,以及如何处理用户交互。文章以通俗易懂的语言,结合代码示例,引导你理解并实现一个简单天气应用的核心功能。无论你是编程新手还是希望扩展你的iOS开发技能,这篇文章都将为你提供宝贵的指导和启发。
|
24天前
|
开发工具 Android开发 iOS开发
探索安卓与iOS开发的差异:构建未来应用的关键考量
在数字时代的浪潮中,安卓和iOS这两大操作系统如同双子星座般耀眼夺目,引领着移动应用的潮流。它们各自拥有独特的魅力和深厚的用户基础,为开发者提供了广阔的舞台。然而,正如每枚硬币都有两面,安卓与iOS在开发过程中也展现出了截然不同的特性。本文将深入剖析这两者在开发环境、编程语言、用户体验设计等方面的显著差异,并探讨如何根据目标受众和项目需求做出明智的选择。无论你是初涉移动应用开发的新手,还是寻求拓展技能边界的资深开发者,这篇文章都将为你提供宝贵的见解和实用的建议,帮助你在安卓与iOS的开发之路上更加从容自信地前行。
|
1月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
108 0