区块链技术博客
www.b2bchain.cn

WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用求职学习资料

本文介绍了WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

  • 写在开头
  • Widgets (小组件) — 前言
    • 介绍
      • 定义一个 Widget
        • 1. 使用的库
        • 2. 配置类型 (Configuration)
        • 3. Widgets 大小支持
        • 4. Placeholder 占位 View
        • 如何建立一个 Widget
      • 创造一个一目了然的 Widget 体验
        • 较为傻瓜的 UI
        • 点触动作
  • Widgets 设计:变更时间线 — 正片
    • 前言
    • Family|Widgets 的不同大小
      • 概述
      • 实现方法
      • 小结
    • Timeline|时间线
      • 概述
      • Timeline 的刷新时机 (Timeline Reload Policy)

写在开头

本文的主题是 WWDC 2020 中 Widgets code-along(小组件编程临摹课程)部分的翻译与解读
本文出自 WWDC 2020 的 https://developer.apple.com/videos/play/wwdc2020/10035/ session

整个《Widgets 边看边写》系列中的其他文章:
WWDC20 10034 – 《Widgets 边看边写》第一部分:冒险开始了
WWDC20 10036 – 《Widgets 边看边写》第三部分:Timelines的进阶使用

Widgets (小组件) — 前言

介绍

如果你来自一下阅读背景 可以根据推荐进行阅读以最高效率的阅读此文

  1. 对 Widgets 的有一定了解
    1. 如果对 widget 基本概念和原理已经有过了解,可以跳过此部分直接阅读 WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用 的讲解部分
    2. 如果你是从 Widgets 边看边写 part.1 或 part.3 点进本文的 你也可以直接从 WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用 开始阅读以得到一个完整流畅的 Widgets 边看边写的体验
  2. 对 Widgets 还不是很了解
    如果你还不是很了解 Widgets 你可以选择:
    1. WWDC20 10028 – WidgetKit 速览 对 Widgets 的功能与使用场景进行了完整详细的介绍
    2. 阅读本文开头的 Widgets 简介部分快速了解 Widgets 的类型与功能

定义一个 Widget

1. 使用的库

  • WidgetKit extension

2. 配置类型 (Configuration)

  • 静态配置 (Static configuration): 显示内容固定,不需要/不允许用户自己设置

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

  • 动态配置 (Intent configuration): 用户可以自己选择当前 Widget 显示的内容

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

用户可以根据需求 选择在 Widget 中显示某一个特定提醒事项

3. Widgets 大小支持

  • 支持多种大小的 Widget ,并且在 iOS,iPadOS,和 macOS 中都支持这三种大小的显示模式

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

4. Placeholder 占位 View

  • Placeholder UI: 默认的显示内容
  • 不包含任何用户数据
  • 根据设备环境不同而改变

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

如何建立一个 Widget

@main public struct SampleWidget: Widget {   private let kind: String = "SampleWidget"    public var body: some WidgetConfiguration {     StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in        SampleWidgetEntryView(entry: entry)     }   .configurationDisplayName("My Widget")   .description("This is an example widget.")   } }

创造一个一目了然的 Widget 体验

较为傻瓜的 UI

  • Widget 不是迷你 APP
  • 不支持滑动
  • 不支持视频或动图
  • 简单的点触动作 直接通向APP入口 (Deep links)

点触动作

  • Widget frame 内任何一点 tap 统一指向同一页面(支持 small/medium/large 三种大小的 widget)

  • – 点触动作直接联入APP内对应的页面(通过 widgetURL)

  • – 如下图:整个 Widget 可以看作一个大按钮 点触按钮会触发 widgetURL 联入 APP 内对应页面

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

  • Widget 内含有多个按钮 分别指向 APP 内的不同页面(只支持medium/large大小的 Widgets )

  • – 如下图:medium widget 中包含两个按钮 分别拥有一个链接指向 APP 内的两个不同页面

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用


(以上是 Widgets 的介绍部分,下面是正文部分,将详细介绍 Widgets 中 Timeline 的使用方法)


Widgets 设计:变更时间线 — 正片

前言

虽然这篇的主题是“变更时间线“,但是这期视频除了介绍了时间线的使用方法外,实际也包括了其他介个关于 Widgets 特性及功能。我们就与视频中的大纲保持一致,从以下几个章节对 Widgets 进行进一步的探索。

  • Family|Widgets 的不同大小

  • Timeline|时间线

  • Configuration|配置类型

  • Deep Link|Widgets 与 APP 内某页面的链接

Family|Widgets 的不同大小

概述

在《Widgets 边看边写》part. 1 中,我们大概了解了 Widgets 的各个代码组成部分并实现了 small size 的 Widget 在主屏幕中的展示。然而就像上面的介绍中所说的那样,Widgets 可以以 小 中 大 三种大小展示在主屏幕上。在介绍时间线的使用方法之前,我们先来看看是如何实现在这三种大小中动态生成显示内容的。

实现方法

这部分内容比较直观,通过代码很容易理解,我们就直接通过代码来看看是如何在不同大小下显示不同的 View 内容的。

我们这里需要根据 Widget 大小改变的显示内容有两个:

  1. 正常显示情况下的 Widget 的 View
  2. Placeholder 中的 View

回顾一下 part. 1 中一个 Widget 里的代码结构,发现决定上面两个 View 的显示内容的是这两个 struct

  • 正常显示情况下的 Widget 的 View
struct EmojiRangerWidgetEntryView: View {   var entry: Provider.Entry   var body: some View {     AvatarView(entry.character) // 这里生成的内容决定了 Widget 显示出的内容   } }
  • Placeholder 中的 View
struct PlaceholderView: View {   var body: some View {     AvatarView(.panda) // 这里生成的内容决定了 Placeholder 显示出的内容   } }

同时,一个 Widget 能够支持的大小也需要手动在 Widget 自己的 struct 中进行配置

@main struct EmojiRangerWidget: Widget {   private let kind: String = "EmojiRangerWidget"   public var body: some WidgetConfiguration {     StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in       EmojiRangerWidgetEntryView(entry: entry)     }     .configurationDisplayName("Ranger Detail")     .description("See your favorite ranger.")     .supportedFamilies([.systemSmall]) // 在 part. 1 中我们仅支持了 systemSmall   } }

因此我们要做的就是把这个两个写死的 AvatarView 变成根据 Widget 大小变换的动态的 View 并在 Widget 支持的大小中加入 systemMedium。修改 View 的方法很简单,我们先用 swiftUI 中提供的 @Environment 获取当前 widget 的大小。根据这个大小再用 switch 来返回不同的 View。

  • 正常显示情况下的 Widget 的 View
struct EmojiRangerWidgetEntryView: View {   var entry: Provider.Entry    @Environment(.widgetFamily) var family    @ViewBuilder   var body: some View {     switch family {     case .systemSmall:         AvatarView(entry.character)           .foregroundColor(.white)       .background(Color.gameBackground)     default:       ZStack {         HStack(alignment: .top) {           AvatarView(entry.character)             .foregroundColor(.white)           Text(entry.character.bio)             .padding()             .foregroundColor(.white)         }         .padding()         //.widgetURL(entry.character.url)       }       .background(Color.gameBackground)     }   } }

** 这里需要注意,官方的 demo 中包含了上面注释掉的这一行,但是因为这个 url 还没有配置,加入这行会导致 Preview 显示不出东西.. 吐槽下这次官方的 demo 真是漏洞百出,使用了很多还没有支持的方法

  • Placeholder 中的 View(这里只需要将之前的 AvatarView 改成上面修改过的 EntryView 就可以)
struct PlaceholderView: View {   var body: some View {     EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), character: .panda))       //.isPlaceholder(true)   } }

**上面注释掉的代码是用来判定当前 View 是否是用作 Placeholder 的,当前版本的 Xcode 还没有支持这个方法,所以暂时将此条注释掉。这也导致 Preview 中的 Placeholder 并不会已 Placeholder 的样式展示出来。

下面就是进行了修改后 Preview 中显示出的样式,可以看出在 medium 大小下已经变成我们想要的左边 Avatar 右边文字的样式了(因为 Placeholder 无法正常显示,把官方 demo 中的 Preview 截图放在了右边,支持 .isPlaceholder() 之后的 Widget 正常显示效果应参考右图)。

当前项目中显示的效果 支持 .isPlaceholder() 后显示的效果
WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用 WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

小结

至此,我们知道了如何对小、中、大三种大小的 Widget 赋予不同的显示内容,接下来我们将把目光方向另一个维度——如何对不同时间的 Widget 赋予不同的显示内容,这也是本文最重要的部分。

**想更详细的了解使用 SwiftUI 给 Widgets 制作 Views 的方法,可以参考这篇文章: WWDC20 – Build SwiftUI views for widgets


Timeline|时间线

概述

Timeline 的作用是使 Widgets 在不同的时间展示不同的内容,一条 Timeline 中含有若干个 entry ,每个 entry 代表一种显示内容。为了保证 Widgets 占用尽量找的系统性能,Widget 根据 Timeline 刷新的时间由系统统一决定。

Timeline 的刷新时机 (Timeline Reload Policy)

项目中的 TimelineProvider 给 Timeline 提供的 Entry 数量是有限的。也就是说在一天内只有一部分有限的时刻 Widgets 会根据 TimelineProvider 中的计划刷新显示内容,而当 entry 消耗殆尽后 Widgets 中的显示内容将不再会根据时间而变化了。

e.g. 参考 demo 中的这个例子,在倒计时结束后角色会恢复体力,那么倒计时清零之后就需要 Reload 时间线了,那么这个 Reload 的时机是怎么确定的呢?

为了更好的理解 Timeline 的生命周期和 Reload 的触发时机,我们可以借助下面这张图片继续学习:
WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

我们可以看到,Timeline 刷新的时机包括以下几种:

  • atEnd

  • after(date)

  • never

那么他们之间的区别是什么呢?

  • atEnd

    • 这个就是在 TimelineProvider 提供的所有 entry 显示完毕之后自动刷新,也就是说只要还有没有显示的 entry 在就不会刷新当前时间线
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用
  • after(date)

    • date 这个参数可以是某一个特定时间值,在系统时间到达那一时刻后会使 Timeline 自动刷新
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用
  • never

    • 这个就是字面意思,永远不会刷新 Timeline,最后一个 entry 也展示完毕之后 Widget 就会一直保持那个 entry 的显示内容
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

这里要注意的是 Timeline Reload 的时机是由系统统一控制的,而为了保证性能,系统会根据各个 Reload 请求的重要等级来决定在某一时刻是否按照 APP 要求的刷新时机来刷新 Timeline。因此如果过于频繁的请求刷新 Timeline,很有可能会被系统限制从而不能达到理想的刷新效果。换句话说,上面所说的 atEnd, after(date) 中定义的刷新 Timeline 的时机可以看作刷新 Timeline 的最早时间,而根据系统的安排,这些时机可能会被延后

  • 写在开头
  • Widgets (小组件) — 前言
    • 介绍
      • 定义一个 Widget
        • 1. 使用的库
        • 2. 配置类型 (Configuration)
        • 3. Widgets 大小支持
        • 4. Placeholder 占位 View
        • 如何建立一个 Widget
      • 创造一个一目了然的 Widget 体验
        • 较为傻瓜的 UI
        • 点触动作
  • Widgets 设计:变更时间线 — 正片
    • 前言
    • Family|Widgets 的不同大小
      • 概述
      • 实现方法
      • 小结
    • Timeline|时间线
      • 概述
      • Timeline 的刷新时机 (Timeline Reload Policy)

写在开头

本文的主题是 WWDC 2020 中 Widgets code-along(小组件编程临摹课程)部分的翻译与解读
本文出自 WWDC 2020 的 https://developer.apple.com/videos/play/wwdc2020/10035/ session

整个《Widgets 边看边写》系列中的其他文章:
WWDC20 10034 – 《Widgets 边看边写》第一部分:冒险开始了
WWDC20 10036 – 《Widgets 边看边写》第三部分:Timelines的进阶使用

Widgets (小组件) — 前言

介绍

如果你来自一下阅读背景 可以根据推荐进行阅读以最高效率的阅读此文

  1. 对 Widgets 的有一定了解
    1. 如果对 widget 基本概念和原理已经有过了解,可以跳过此部分直接阅读 WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用 的讲解部分
    2. 如果你是从 Widgets 边看边写 part.1 或 part.3 点进本文的 你也可以直接从 WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用 开始阅读以得到一个完整流畅的 Widgets 边看边写的体验
  2. 对 Widgets 还不是很了解
    如果你还不是很了解 Widgets 你可以选择:
    1. WWDC20 10028 – WidgetKit 速览 对 Widgets 的功能与使用场景进行了完整详细的介绍
    2. 阅读本文开头的 Widgets 简介部分快速了解 Widgets 的类型与功能

定义一个 Widget

1. 使用的库

  • WidgetKit extension

2. 配置类型 (Configuration)

  • 静态配置 (Static configuration): 显示内容固定,不需要/不允许用户自己设置

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

  • 动态配置 (Intent configuration): 用户可以自己选择当前 Widget 显示的内容

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

用户可以根据需求 选择在 Widget 中显示某一个特定提醒事项

3. Widgets 大小支持

  • 支持多种大小的 Widget ,并且在 iOS,iPadOS,和 macOS 中都支持这三种大小的显示模式

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

4. Placeholder 占位 View

  • Placeholder UI: 默认的显示内容
  • 不包含任何用户数据
  • 根据设备环境不同而改变

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

如何建立一个 Widget

@main public struct SampleWidget: Widget {   private let kind: String = "SampleWidget"    public var body: some WidgetConfiguration {     StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in        SampleWidgetEntryView(entry: entry)     }   .configurationDisplayName("My Widget")   .description("This is an example widget.")   } }

创造一个一目了然的 Widget 体验

较为傻瓜的 UI

  • Widget 不是迷你 APP
  • 不支持滑动
  • 不支持视频或动图
  • 简单的点触动作 直接通向APP入口 (Deep links)

点触动作

  • Widget frame 内任何一点 tap 统一指向同一页面(支持 small/medium/large 三种大小的 widget)

  • – 点触动作直接联入APP内对应的页面(通过 widgetURL)

  • – 如下图:整个 Widget 可以看作一个大按钮 点触按钮会触发 widgetURL 联入 APP 内对应页面

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

  • Widget 内含有多个按钮 分别指向 APP 内的不同页面(只支持medium/large大小的 Widgets )

  • – 如下图:medium widget 中包含两个按钮 分别拥有一个链接指向 APP 内的两个不同页面

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用


(以上是 Widgets 的介绍部分,下面是正文部分,将详细介绍 Widgets 中 Timeline 的使用方法)


Widgets 设计:变更时间线 — 正片

前言

虽然这篇的主题是“变更时间线“,但是这期视频除了介绍了时间线的使用方法外,实际也包括了其他介个关于 Widgets 特性及功能。我们就与视频中的大纲保持一致,从以下几个章节对 Widgets 进行进一步的探索。

  • Family|Widgets 的不同大小

  • Timeline|时间线

  • Configuration|配置类型

  • Deep Link|Widgets 与 APP 内某页面的链接

Family|Widgets 的不同大小

概述

在《Widgets 边看边写》part. 1 中,我们大概了解了 Widgets 的各个代码组成部分并实现了 small size 的 Widget 在主屏幕中的展示。然而就像上面的介绍中所说的那样,Widgets 可以以 小 中 大 三种大小展示在主屏幕上。在介绍时间线的使用方法之前,我们先来看看是如何实现在这三种大小中动态生成显示内容的。

实现方法

这部分内容比较直观,通过代码很容易理解,我们就直接通过代码来看看是如何在不同大小下显示不同的 View 内容的。

我们这里需要根据 Widget 大小改变的显示内容有两个:

  1. 正常显示情况下的 Widget 的 View
  2. Placeholder 中的 View

回顾一下 part. 1 中一个 Widget 里的代码结构,发现决定上面两个 View 的显示内容的是这两个 struct

  • 正常显示情况下的 Widget 的 View
struct EmojiRangerWidgetEntryView: View {   var entry: Provider.Entry   var body: some View {     AvatarView(entry.character) // 这里生成的内容决定了 Widget 显示出的内容   } }
  • Placeholder 中的 View
struct PlaceholderView: View {   var body: some View {     AvatarView(.panda) // 这里生成的内容决定了 Placeholder 显示出的内容   } }

同时,一个 Widget 能够支持的大小也需要手动在 Widget 自己的 struct 中进行配置

@main struct EmojiRangerWidget: Widget {   private let kind: String = "EmojiRangerWidget"   public var body: some WidgetConfiguration {     StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in       EmojiRangerWidgetEntryView(entry: entry)     }     .configurationDisplayName("Ranger Detail")     .description("See your favorite ranger.")     .supportedFamilies([.systemSmall]) // 在 part. 1 中我们仅支持了 systemSmall   } }

因此我们要做的就是把这个两个写死的 AvatarView 变成根据 Widget 大小变换的动态的 View 并在 Widget 支持的大小中加入 systemMedium。修改 View 的方法很简单,我们先用 swiftUI 中提供的 @Environment 获取当前 widget 的大小。根据这个大小再用 switch 来返回不同的 View。

  • 正常显示情况下的 Widget 的 View
struct EmojiRangerWidgetEntryView: View {   var entry: Provider.Entry    @Environment(.widgetFamily) var family    @ViewBuilder   var body: some View {     switch family {     case .systemSmall:         AvatarView(entry.character)           .foregroundColor(.white)       .background(Color.gameBackground)     default:       ZStack {         HStack(alignment: .top) {           AvatarView(entry.character)             .foregroundColor(.white)           Text(entry.character.bio)             .padding()             .foregroundColor(.white)         }         .padding()         //.widgetURL(entry.character.url)       }       .background(Color.gameBackground)     }   } }

** 这里需要注意,官方的 demo 中包含了上面注释掉的这一行,但是因为这个 url 还没有配置,加入这行会导致 Preview 显示不出东西.. 吐槽下这次官方的 demo 真是漏洞百出,使用了很多还没有支持的方法

  • Placeholder 中的 View(这里只需要将之前的 AvatarView 改成上面修改过的 EntryView 就可以)
struct PlaceholderView: View {   var body: some View {     EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), character: .panda))       //.isPlaceholder(true)   } }

**上面注释掉的代码是用来判定当前 View 是否是用作 Placeholder 的,当前版本的 Xcode 还没有支持这个方法,所以暂时将此条注释掉。这也导致 Preview 中的 Placeholder 并不会已 Placeholder 的样式展示出来。

下面就是进行了修改后 Preview 中显示出的样式,可以看出在 medium 大小下已经变成我们想要的左边 Avatar 右边文字的样式了(因为 Placeholder 无法正常显示,把官方 demo 中的 Preview 截图放在了右边,支持 .isPlaceholder() 之后的 Widget 正常显示效果应参考右图)。

当前项目中显示的效果 支持 .isPlaceholder() 后显示的效果
WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用 WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

小结

至此,我们知道了如何对小、中、大三种大小的 Widget 赋予不同的显示内容,接下来我们将把目光方向另一个维度——如何对不同时间的 Widget 赋予不同的显示内容,这也是本文最重要的部分。

**想更详细的了解使用 SwiftUI 给 Widgets 制作 Views 的方法,可以参考这篇文章: WWDC20 – Build SwiftUI views for widgets


Timeline|时间线

概述

Timeline 的作用是使 Widgets 在不同的时间展示不同的内容,一条 Timeline 中含有若干个 entry ,每个 entry 代表一种显示内容。为了保证 Widgets 占用尽量找的系统性能,Widget 根据 Timeline 刷新的时间由系统统一决定。

Timeline 的刷新时机 (Timeline Reload Policy)

项目中的 TimelineProvider 给 Timeline 提供的 Entry 数量是有限的。也就是说在一天内只有一部分有限的时刻 Widgets 会根据 TimelineProvider 中的计划刷新显示内容,而当 entry 消耗殆尽后 Widgets 中的显示内容将不再会根据时间而变化了。

e.g. 参考 demo 中的这个例子,在倒计时结束后角色会恢复体力,那么倒计时清零之后就需要 Reload 时间线了,那么这个 Reload 的时机是怎么确定的呢?

为了更好的理解 Timeline 的生命周期和 Reload 的触发时机,我们可以借助下面这张图片继续学习:
WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

我们可以看到,Timeline 刷新的时机包括以下几种:

  • atEnd

  • after(date)

  • never

那么他们之间的区别是什么呢?

  • atEnd

    • 这个就是在 TimelineProvider 提供的所有 entry 显示完毕之后自动刷新,也就是说只要还有没有显示的 entry 在就不会刷新当前时间线
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用
  • after(date)

    • date 这个参数可以是某一个特定时间值,在系统时间到达那一时刻后会使 Timeline 自动刷新
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用
  • never

    • 这个就是字面意思,永远不会刷新 Timeline,最后一个 entry 也展示完毕之后 Widget 就会一直保持那个 entry 的显示内容
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

这里要注意的是 Timeline Reload 的时机是由系统统一控制的,而为了保证性能,系统会根据各个 Reload 请求的重要等级来决定在某一时刻是否按照 APP 要求的刷新时机来刷新 Timeline。因此如果过于频繁的请求刷新 Timeline,很有可能会被系统限制从而不能达到理想的刷新效果。换句话说,上面所说的 atEnd, after(date) 中定义的刷新 Timeline 的时机可以看作刷新 Timeline 的最早时间,而根据系统的安排,这些时机可能会被延后

  • 写在开头
  • Widgets (小组件) — 前言
    • 介绍
      • 定义一个 Widget
        • 1. 使用的库
        • 2. 配置类型 (Configuration)
        • 3. Widgets 大小支持
        • 4. Placeholder 占位 View
        • 如何建立一个 Widget
      • 创造一个一目了然的 Widget 体验
        • 较为傻瓜的 UI
        • 点触动作
  • Widgets 设计:变更时间线 — 正片
    • 前言
    • Family|Widgets 的不同大小
      • 概述
      • 实现方法
      • 小结
    • Timeline|时间线
      • 概述
      • Timeline 的刷新时机 (Timeline Reload Policy)

写在开头

本文的主题是 WWDC 2020 中 Widgets code-along(小组件编程临摹课程)部分的翻译与解读
本文出自 WWDC 2020 的 https://developer.apple.com/videos/play/wwdc2020/10035/ session

整个《Widgets 边看边写》系列中的其他文章:
WWDC20 10034 – 《Widgets 边看边写》第一部分:冒险开始了
WWDC20 10036 – 《Widgets 边看边写》第三部分:Timelines的进阶使用

Widgets (小组件) — 前言

介绍

如果你来自一下阅读背景 可以根据推荐进行阅读以最高效率的阅读此文

  1. 对 Widgets 的有一定了解
    1. 如果对 widget 基本概念和原理已经有过了解,可以跳过此部分直接阅读 WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用 的讲解部分
    2. 如果你是从 Widgets 边看边写 part.1 或 part.3 点进本文的 你也可以直接从 WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用 开始阅读以得到一个完整流畅的 Widgets 边看边写的体验
  2. 对 Widgets 还不是很了解
    如果你还不是很了解 Widgets 你可以选择:
    1. WWDC20 10028 – WidgetKit 速览 对 Widgets 的功能与使用场景进行了完整详细的介绍
    2. 阅读本文开头的 Widgets 简介部分快速了解 Widgets 的类型与功能

定义一个 Widget

1. 使用的库

  • WidgetKit extension

2. 配置类型 (Configuration)

  • 静态配置 (Static configuration): 显示内容固定,不需要/不允许用户自己设置

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

  • 动态配置 (Intent configuration): 用户可以自己选择当前 Widget 显示的内容

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

用户可以根据需求 选择在 Widget 中显示某一个特定提醒事项

3. Widgets 大小支持

  • 支持多种大小的 Widget ,并且在 iOS,iPadOS,和 macOS 中都支持这三种大小的显示模式

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

4. Placeholder 占位 View

  • Placeholder UI: 默认的显示内容
  • 不包含任何用户数据
  • 根据设备环境不同而改变

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

如何建立一个 Widget

@main public struct SampleWidget: Widget {   private let kind: String = "SampleWidget"    public var body: some WidgetConfiguration {     StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in        SampleWidgetEntryView(entry: entry)     }   .configurationDisplayName("My Widget")   .description("This is an example widget.")   } }

创造一个一目了然的 Widget 体验

较为傻瓜的 UI

  • Widget 不是迷你 APP
  • 不支持滑动
  • 不支持视频或动图
  • 简单的点触动作 直接通向APP入口 (Deep links)

点触动作

  • Widget frame 内任何一点 tap 统一指向同一页面(支持 small/medium/large 三种大小的 widget)

  • – 点触动作直接联入APP内对应的页面(通过 widgetURL)

  • – 如下图:整个 Widget 可以看作一个大按钮 点触按钮会触发 widgetURL 联入 APP 内对应页面

WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

  • Widget 内含有多个按钮 分别指向 APP 内的不同页面(只支持medium/large大小的 Widgets )

  • – 如下图:medium widget 中包含两个按钮 分别拥有一个链接指向 APP 内的两个不同页面

    WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用


(以上是 Widgets 的介绍部分,下面是正文部分,将详细介绍 Widgets 中 Timeline 的使用方法)


Widgets 设计:变更时间线 — 正片

前言

虽然这篇的主题是“变更时间线“,但是这期视频除了介绍了时间线的使用方法外,实际也包括了其他介个关于 Widgets 特性及功能。我们就与视频中的大纲保持一致,从以下几个章节对 Widgets 进行进一步的探索。

  • Family|Widgets 的不同大小

  • Timeline|时间线

  • Configuration|配置类型

  • Deep Link|Widgets 与 APP 内某页面的链接

Family|Widgets 的不同大小

概述

在《Widgets 边看边写》part. 1 中,我们大概了解了 Widgets 的各个代码组成部分并实现了 small size 的 Widget 在主屏幕中的展示。然而就像上面的介绍中所说的那样,Widgets 可以以 小 中 大 三种大小展示在主屏幕上。在介绍时间线的使用方法之前,我们先来看看是如何实现在这三种大小中动态生成显示内容的。

实现方法

这部分内容比较直观,通过代码很容易理解,我们就直接通过代码来看看是如何在不同大小下显示不同的 View 内容的。

我们这里需要根据 Widget 大小改变的显示内容有两个:

  1. 正常显示情况下的 Widget 的 View
  2. Placeholder 中的 View

回顾一下 part. 1 中一个 Widget 里的代码结构,发现决定上面两个 View 的显示内容的是这两个 struct

  • 正常显示情况下的 Widget 的 View
struct EmojiRangerWidgetEntryView: View {   var entry: Provider.Entry   var body: some View {     AvatarView(entry.character) // 这里生成的内容决定了 Widget 显示出的内容   } }
  • Placeholder 中的 View
struct PlaceholderView: View {   var body: some View {     AvatarView(.panda) // 这里生成的内容决定了 Placeholder 显示出的内容   } }

同时,一个 Widget 能够支持的大小也需要手动在 Widget 自己的 struct 中进行配置

@main struct EmojiRangerWidget: Widget {   private let kind: String = "EmojiRangerWidget"   public var body: some WidgetConfiguration {     StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in       EmojiRangerWidgetEntryView(entry: entry)     }     .configurationDisplayName("Ranger Detail")     .description("See your favorite ranger.")     .supportedFamilies([.systemSmall]) // 在 part. 1 中我们仅支持了 systemSmall   } }

因此我们要做的就是把这个两个写死的 AvatarView 变成根据 Widget 大小变换的动态的 View 并在 Widget 支持的大小中加入 systemMedium。修改 View 的方法很简单,我们先用 swiftUI 中提供的 @Environment 获取当前 widget 的大小。根据这个大小再用 switch 来返回不同的 View。

  • 正常显示情况下的 Widget 的 View
struct EmojiRangerWidgetEntryView: View {   var entry: Provider.Entry    @Environment(.widgetFamily) var family    @ViewBuilder   var body: some View {     switch family {     case .systemSmall:         AvatarView(entry.character)           .foregroundColor(.white)       .background(Color.gameBackground)     default:       ZStack {         HStack(alignment: .top) {           AvatarView(entry.character)             .foregroundColor(.white)           Text(entry.character.bio)             .padding()             .foregroundColor(.white)         }         .padding()         //.widgetURL(entry.character.url)       }       .background(Color.gameBackground)     }   } }

** 这里需要注意,官方的 demo 中包含了上面注释掉的这一行,但是因为这个 url 还没有配置,加入这行会导致 Preview 显示不出东西.. 吐槽下这次官方的 demo 真是漏洞百出,使用了很多还没有支持的方法

  • Placeholder 中的 View(这里只需要将之前的 AvatarView 改成上面修改过的 EntryView 就可以)
struct PlaceholderView: View {   var body: some View {     EmojiRangerWidgetEntryView(entry: SimpleEntry(date: Date(), character: .panda))       //.isPlaceholder(true)   } }

**上面注释掉的代码是用来判定当前 View 是否是用作 Placeholder 的,当前版本的 Xcode 还没有支持这个方法,所以暂时将此条注释掉。这也导致 Preview 中的 Placeholder 并不会已 Placeholder 的样式展示出来。

下面就是进行了修改后 Preview 中显示出的样式,可以看出在 medium 大小下已经变成我们想要的左边 Avatar 右边文字的样式了(因为 Placeholder 无法正常显示,把官方 demo 中的 Preview 截图放在了右边,支持 .isPlaceholder() 之后的 Widget 正常显示效果应参考右图)。

当前项目中显示的效果 支持 .isPlaceholder() 后显示的效果
WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用 WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

小结

至此,我们知道了如何对小、中、大三种大小的 Widget 赋予不同的显示内容,接下来我们将把目光方向另一个维度——如何对不同时间的 Widget 赋予不同的显示内容,这也是本文最重要的部分。

**想更详细的了解使用 SwiftUI 给 Widgets 制作 Views 的方法,可以参考这篇文章: WWDC20 – Build SwiftUI views for widgets


Timeline|时间线

概述

Timeline 的作用是使 Widgets 在不同的时间展示不同的内容,一条 Timeline 中含有若干个 entry ,每个 entry 代表一种显示内容。为了保证 Widgets 占用尽量找的系统性能,Widget 根据 Timeline 刷新的时间由系统统一决定。

Timeline 的刷新时机 (Timeline Reload Policy)

项目中的 TimelineProvider 给 Timeline 提供的 Entry 数量是有限的。也就是说在一天内只有一部分有限的时刻 Widgets 会根据 TimelineProvider 中的计划刷新显示内容,而当 entry 消耗殆尽后 Widgets 中的显示内容将不再会根据时间而变化了。

e.g. 参考 demo 中的这个例子,在倒计时结束后角色会恢复体力,那么倒计时清零之后就需要 Reload 时间线了,那么这个 Reload 的时机是怎么确定的呢?

为了更好的理解 Timeline 的生命周期和 Reload 的触发时机,我们可以借助下面这张图片继续学习:
WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

我们可以看到,Timeline 刷新的时机包括以下几种:

  • atEnd

  • after(date)

  • never

那么他们之间的区别是什么呢?

  • atEnd

    • 这个就是在 TimelineProvider 提供的所有 entry 显示完毕之后自动刷新,也就是说只要还有没有显示的 entry 在就不会刷新当前时间线
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用
  • after(date)

    • date 这个参数可以是某一个特定时间值,在系统时间到达那一时刻后会使 Timeline 自动刷新
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用
  • never

    • 这个就是字面意思,永远不会刷新 Timeline,最后一个 entry 也展示完毕之后 Widget 就会一直保持那个 entry 的显示内容
      WWDC20 10035 - 《Widgets 边看边写》第二部分:Timelines 的基本使用

这里要注意的是 Timeline Reload 的时机是由系统统一控制的,而为了保证性能,系统会根据各个 Reload 请求的重要等级来决定在某一时刻是否按照 APP 要求的刷新时机来刷新 Timeline。因此如果过于频繁的请求刷新 Timeline,很有可能会被系统限制从而不能达到理想的刷新效果。换句话说,上面所说的 atEnd, after(date) 中定义的刷新 Timeline 的时机可以看作刷新 Timeline 的最早时间,而根据系统的安排,这些时机可能会被延后

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » WWDC20 10035 – 《Widgets 边看边写》第二部分:Timelines 的基本使用求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们