SwiftUI:跨语言、跨平台、小组件

原创:知识点总结性文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 一、跨国应用本地化
    • 1、商店描述
    • 2、UI 界面
    • 3、应用名称
    • 4、交给别人翻译
  • 二、跨平台应用
    • 1、创建跨平台应用
    • 2、Mac 应用版本间的差异
    • 3、区分 iPhone 及 Mac 平台
    • 4、区分 iPadOS 及 iOS 平台
    • 5、Apple Watch 应用
    • 6、Apple TV 应用
    • 7、判断 UI 在各平台上的可用性
  • 三、桌面小组件 Widgets
    • 1、背景
    • 2、系统级别的功能
    • 3、制作桌面小组件的流程
    • 4、代码详解

一、跨国应用本地化

应用本地化 Localization,主要指的是将现有应用,根据不同地区的语言习惯翻译为不同语种。除此之外,它还意味着对当地的使用习惯进行针对性调整,比如阅读语序等。SwiftUI 已经自动处理好了诸如文本框中的使用方向,导航器的呼出方向等问题,因此独立开发者主要需要做的本例化工作便是翻译。

在设计一款目标发售地包含国际市场的应用时,你需要务必留意所有字符串的长度。中文作为一种用方块文字表达意思的语言,其简短的词组便可以表达完整意思。而世界上很多语言是拉丁语系的,主要是用词根和词缀来对单词进行修饰,甚至在不少语言中物品还存在男女的叫法,因此描述起来非常长。比如某个单词在中文中只需要两个字符的位置,在德语中可能需要十几个字符位。

考虑到语言上的差异,在设计应用程序界面时应该留足空位,以便让不同语言能完整显示出来。对于界面元素,能用 SwiftUI 的就用,尽量避免直接从设计软件中直接导入带文字内容的界面元素,这样会给日后的本地化工作平添麻烦。

应用程序的核心逻辑常不受语言限制,将应用翻译为不同语言,十分有助于扩展销路。通常来说,欧洲、美国、中国这三大市场竞争较为激烈,想要让应用获得较好的排位相对困难。但竞争激烈的同时,这些市场的回报也最高,值得为其所在地的语言进行应用翻译工作。

对于较为小众的市场,诸如非洲或南亚各国,通常消费力更低一些。Apple 本身的产品渗透率在这些地区也稍差,这时候能做到多少,要做多少就得由你自己进行取舍了。但有一点是确定的,市场再小也是市场,且这些小众市场往往竞争相对缓和,若你能对他们的语言进行针对性优化,成功的可能性反而更大些。

本文将以支持「英文、中文」的应用为例,介绍应用本地化工作的方法。

1、商店描述

应用程序在 App Store 中的全部描述由 App Store Connect 网站负责。如下图所示,当你希望添加不同语种的应用简介、更新公告时,可以点击下图右侧的语言按钮。选中语言过后,点击右侧的「+」按钮来新增应用描述所支持的语言。

部分人只会翻译应用描述,却忽略了预览图部分。若你在对主要的市场进行翻译工作时,强烈建议一并更新预览图。假设不对预览图进行单独修改,则 App Store 会默认使用默认语言,如英文的预览图在全球的商店显示。

若你需要对预览截图进行本地化,可以长按应用程序图标,并选择「Edit Scheme」。在弹出的下列界面中,找到「App Language 应用语言」,将其修改为待截图的目标语言即可。

在 App Store Connect 的语言列表中,你会看到某个语言带有「Primary」标识,如下图所示。在当前应用中,Primary 语言指的是在开发者没有提供对应内容的本地化翻译时,任何素材、简介、截图等信息的默认显示语言。

应用名称、标题的翻译在 App Store Connect 的「App Information 应用信息」分区中,如下图所示。点击右侧的语言按钮,可以在不同语言中切换。在上文中已添加中文,因此下图中也已经包含了中文选项。切换至不同语言后,将翻译填入下图表格中即可。


2、UI 界面

应用启动后,显示出的任何文字都可以说是 UI 界面的一部分,翻译这一部分内容至关重要。在 Xcode 中依次选中「项目名 - Project 标签页」,如下图所示。下图中包含一个 Localization 的选项,此处默认情况下只有一个叫做 English - Development Language 的选项,你可以把英文当作代码层面的默认设置。想要添加新的语言支持,点击「+」即可,本例中我选择添加了简体中文 Simplified Chinese

Xcode 对 SwiftUI 中 UI 本地化流程有点像是查询电话号码本。该电话号码本的文件类型为 Strings File。如下图所示,新建一个 Strings File

将该文件命名为 Localizable.strings,代表 UI 翻译文件,之后点击「Create」创建。

创建完成后,Xcode 会提供默认英文版的电话号码本,如下图所示。但我们还额外需要中文的电话号码本,因此点击下图中右侧的「Localize 本地化」按钮。

点击 Localize 后,在下图右侧弹出的本地化选项中勾选英文和中文两种语言。勾选完成后,Xcode 会自动在左侧创建两个号码本,一个是英文版,一个是中文版,如下图所示。

在 SwiftUI 中,所有文本框中输入的字符,都会被 Xcode 视作是在号码本中查询的钥匙。比如下图的简单范例中,UI 是一个纯文本视图,显示「Three Good Things」,此时 Three Good Things 便会被 Xcode 视作钥匙拿去号码本中查询,如果号码本中存在完全匹配的字符串,则直接用号码本中的翻译,如果没有,则直接显示 Three Good Things 这段未经翻译的文字。

想要让 Xcode 知道这段话该如何翻译,你需要依次进入刚才创建好的中文和英文电话号码本,依次输入 Three Good Things 的翻译方式,如下图所示,在中文的号码本中 Three Good Things 会被翻译为三件好事,英文的号码本中会被翻译为 Three Good Things 保持不变。值得注意的是,以中文翻译「"Three Good Things" = "三件好事";」为例,等号左侧的钥匙必须要与 SwiftUI 中输入的字符串完全匹配,等号右侧的则是对应的本地化翻译,结束时必须用到英文分号「;」。

翻译完成后,分别运行中文版和英文版的虚拟机,UI 效果如下。至此应用的 UI 界面完成本地化翻译。


3、应用名称

应用名称的翻译与界面翻译非常类似,不过需要创建一个专门用于查询应用显示名称的号码本。如下图所示,新建一个 Strings File

将该文件命名为「InfoPlist.strings」并创建文件。

与上文类似,点击 Localize 按钮将 Info.Plist 文件分别创建为两种语言的号码本。

打开对应的语言文件并进行翻译,比如「"CFBundleDisplayName" = "三件好事";」会将该应用的中文显示名称改为「三件好事」。本例中,CFBundleDisplayName 指的是 Xcode 用于自动识别应用名称的钥匙。

若你使用上述虚拟机切换语言的方法,会发现应用的显示名本地化没有正确显示。这是因为在 iOS 设备时,所有应用名称不跟随应用自身设置,而是全部跟随系统显示,因此想要查看翻译是否成功,需要在虚拟机的设置中,切换系统语言,如下图中间的截图所示,之后本地化应用名称即可正常显示。


4、交给别人翻译

对于机器翻译来说,我个人比较推荐 DeepL,它的翻译在水准之上,且支持语言较多。笔者之前写过一篇 介绍 DeepL 用法及效果的文章,不再赘述。但还有些情况,你可能会拜托朋友第三方翻译帮你进行本地化工作,这时便需要把文件交给别人,这时便需要分享源文件。打开 Xcode,并右键选择「Show in Finder」。

在打开的 Xcode 项目中,你会看到尾缀是 .lproj 的本地化翻译文件。比如 en 指的就是英文翻译,zh-Hans 指的是中文翻译。假设你希望请别人翻译英文版,只需要把 en.lproj 文件夹分享给他人即可,不需要分享整个 Xcode 项目文件。

以中文为例,本地化文件夹中包含 InfoPlist.strings 应用名称文件,以及 Localizable.strings 应用 UI 文件。若对方的机器是 Windows,或没有 Xcode 的 Mac,你可以请对方用任意的文本编辑器打开进行翻译。以 Mac 为例,右键打开该文件,选择用自带的文本编辑器「TextEdit」打开即可。

打开后的文件如下图所示,修改好翻译之后保存。当开发者拿到翻译完的文件夹后,直接粘贴到 Xcode 项目中覆盖旧的翻译文件夹即可。


二、跨平台应用

随着 Mac 产品线逐渐过渡到 M 系列芯片,SwiftUI 转变成为 Apple 生态系统中全部平台的主要 UI 语言,跨平台应用开发变得前所未有地简单。对于开发者来说,使用 SwiftUI 的标准控件,可以让你无需操心如何定制适用于各个平台的 UI,只需要用一套写法,在每个系统平台中都会自动适配相应的 UI 外观。

1、创建跨平台应用

若你只需要某个平台的应用,可以直接选择对应的「iOS」或「macOS」应用模版进行创建。若你想创建跨 iPad、iOS、macOS 三平台应用,只需要在 Xcode 创建应用的模版中选择「Multiplatform - App」即可,如下图所示。

跨三平台的应用默认使用 SwiftUI 作为 UI 界面语言,使用 SwiftUI Lifecycle 用作生命周期控制,无任何使用其它选择,如下图所示。


2、Mac 应用版本间的差异

未来数年中,会出现英特尔芯片与 M 系列 Mac 机型并存的状态,因此 Mac 版本和运作方式稍显复杂。创建完跨平台应用后,你会注意到应用运行窗口有两个选项,其中之一备注为 iOS,其二备注为 macOS。

这里有个选项你也许会觉得奇怪,在 iOS 大类中,居然出现了 My Mac (Designed for iPad) 选项。这个选项指的是该应用仍然为 iOS 应用,若开发者不进行额外说明,默认支持所有搭载 M 芯片的 Mac 电脑运行。本质上来说,以该模式运行的应用仍然为原版的 iOS 应用。在此模式下,所有 UI 显示方式和操作手势等完全与 iOS 版本相同。

在 macOS 应用的选项中,出现了两个 Mac 对应类型,分别是 My MacMy Mac (Rosetta),均指的是以 Mac 版本的 UI 进行显示。若你正在使用的 Mac 开发机器搭载 M1 芯片,则 My Mac 指的是让应用以 x86 原生模式运行;若你正在使用的 Mac 开发机器搭载 M1 芯片,则 My Mac 指的是让应用以 ARM 原生模式运行。在 M1 版本的机器上,还会额外出现 Rosetta 的选项,指的是应用以 x86 形式编译,通过兼容层在 M 芯片的机器上运行。

若你在应用中用到了较老的 UIKit 作为视图框架,还可能在运行栏中看到 My Mac (Catalyst),指的是将 UIKit 应用以 MacCatalyst 翻译后,运行在 Mac 机型中。请注意,以上所有的选项,均取决于你的开发机器,可能和本文截图有所差异。总的来说,现阶段下 Mac 应用运行共有 6 种方式,分别是:

  • Mac (Designed for iPad):纯 iOS 应用,仅支持 M 芯片版 Mac
  • Mac (Intel):纯 macOS 应用,仅支持 Intel 芯片版 Mac
  • Mac (ARM):纯 macOS 应用,仅支持 M 芯片版 Mac
  • Mac (Rosetta):纯 macOS 应用,支持 M 及 Intel 芯片版 Mac
  • Mac (Catalyst):较老的写法。纯 Mac 版 UIKit 应用,支持 M 及 Intel 芯片版 Mac
  • Mac (Appkit):较老的写法。纯 Appkit 应用,支持 M 及 Intel 芯片版 Mac

3、区分 iPhone 及 Mac 平台

你也许会问,同样的代码,假设我需要创建出两种不同的视图排版怎么办。遇到特别复杂的界面差异,你可以考虑新建一个 SwiftUI 视图,来单独处理某个平台的显示。若你只需要进行一些细微变动,则可以使用语法 #if os(xxx) #endif 来进行针对性判断。如下图所示,如果软件运行在 iOS 设备上,则显示 iOS 文字,反之则显示 macOS 文字。

将包含上述代码所示的应用运行后,结果如下。可以看到,虽然是同一个 SwiftUI 视图,内容根据平台进行了差异化显示。


4、区分 iPadOS 及 iOS 平台

Apple 当下的生态系统命名有些不规范,虽然 iPad 上设备名义上运行的是 iPadOS 操作系统,但是在 Xcode 体系中,仍然被归类为 iOS。因此上述判断方案无法得知应用是运行在 iPhone 还是 iPad 上。此时可以用到另一种判断方法,如果 UIDevice.current.userInterfaceIdiom == .pad 为真,则运行在 iPad 上,反之运行在 iPhone 上,如下图所示。

运行上述代码后,效果如下图所示。可以看到 iOS 和 iPadOS 的代码被区分看待。


5、Apple Watch 应用

若你希望给应用增加 Apple Watch 的手表支持,可以在 Xcode 顶部菜单栏中,选择「File - New Target」,如下图所示。

在 watchOS 菜单中,可以看到「Watch App for iOS App」和「Watch App」两个选项。这二者的区分度需要额外留意,其中 for iOS App 指的是该手表应用必须依赖于 iOS 应用主体发布,无法单独在 Watch 应用商店中下载。无论该手表应用是否存在与 iOS 应用的数据互通,这种依赖关系是必须存在的。而 Watch App 指的是可以单独发布,无需依赖任何 iOS 应用,可直接在 Watch 应用商店中下载的应用。这二者根据你的实际需求选择即可。

选择完成后,如下图所示,将 Watch 应用添加至 Xcode 项目中。

添加完成后,若你选择的是 Watch App for iOS App 选项,则会看到下图左侧多出来了 WatchApp Extension 选项。该文件夹中的内容,是 Xcode 已经为 Watch 应用创建好的模版。以我们熟悉的 ContentView.swift 为例,可以看到如下图所示的 SwiftUI 视图代码。

额外说明一下,你也许会感到疑惑,Xcode 如何知道项目中相同文件名的文件到底该运行哪一份,答案是 Target Membership 运行目标。如下图右侧所示,假设代码的核心逻辑下载在 Logic.swift 文件中,倘若在右侧面板勾选 WatchApp Extension,则代表这份文件可以在 Watch 应用中使用,其它所有文件同理,都具备运行目标这个选项。

结束界面和逻辑的编程后,点击运行栏中的 WatchApp,并在模拟器中选择一台虚拟机即可运行。额外说明的是,若你希望用到真机测试,则需要在 Xcode 中的「Window - Devices and Simulators」中,对手表进行与手机的配对。之后就可以直接在真机手表上进行测试了。

将 Watch 虚拟机一并运行,当前版本的跨平台应用已支持 iPad、iPhone、Apple Watch、Mac 这四个平台。


6、Apple TV 应用

在海外市场中,Apple TV 应用也占据一席之地。你可以将 TV 应用理解为家庭机顶盒,或运行在智能电视中的应用,只不过在 Apple 平台的硬件载体是 Apple TV。重复上述 Apple Watch 应用的添加方法,这次选择 TV App 即可创建 Apple TV 应用。创建完成后,点击 Apple TV 模拟机即可运行,如下图所示。

至此,在同一个 Xcode 项目中添加各平台的代码介绍完毕,如下图所示。Mac、Apple TV、Apple Watch、iPhone、iPad 应用均通过 SwiftUI 界面语言串联在一起。


7、判断 UI 在各平台上的可用性

在开发过程中,你也许会碰到另一个问题,我的 SwiftUI 代码到底哪些可以用。这个问题的答案写在 Apple 开发者文档中,如下图右侧所示,在调用框架时,你需要注意该框架中内容的可用性。比如 Toggle 开关的写法,支持全平台使用。如下图所示,将一个最简单的开关代码放置在跨平台文件夹中。

@State private var toggleOn = true 
Toggle("Do Something", isOn: $toggleOn).padding()

对于视图有差异的 Apple TV,同理将开关代码复制进去,但删除 iOS 的判断语句。

准备完成后运行,可以看到如下图所示的界面。在 Mac、iPhone、Apple Watch、Apple TV 中,同样的 SwiftUI 开关写法,实际上显示方式完全不一样。在 Mac 中是我们熟悉的勾选框,而在 TV 中则是点入式菜单。这也是 SwiftUI 强大的地方,并非追求统一,而是在代码统一的前提下,保证各个平台体验的独立性。

对于很多应用来说,诸如用到 Apple Pencil 的 Procreate,也许纯 iPad 应用就已经是最佳选择;对于笔记应用如 Bear,用户确实有跨平台记录的需求;对于一款快速查看潮汐的应用,也许纯 Apple Watch 应用已经是最佳选择,毕竟在进行这类运动时用户手机大概不在身边。


三、桌面小组件 Widgets

桌面小组件 Widget 是 iOS 14、macOS Big Sur 之后,Apple 推出的显示在桌面上的便捷视图。现阶段,桌面小组件可以被用在 iPhone、iPad、Mac 这三类设备中。Apple 生态系统中的小组件以展示信息为核心诉求,仅提供非常轻量的互动。若你习惯了 Android 手机上的部分复杂强大的桌面组件,应该适当降低预期,Apple 版本的桌面小组件与其还是有较大差异的。

1、背景

以 iPhone 为例,小组件主要集中在设备的负一屏,亦可被添加在桌面的任意位置。当用户长按桌面背景,并点击左上角的加号后,可以看到如下图所示的小组件选择器。小组件选择器罗列了当前设备中所有支持的小组件的应用,并提供了小组件预览,用户点击后即可拖拽添加至桌面。

小组件分为三种尺寸,分别是占据 4 个应用图标格的小尺寸、8 个应用图标格的中尺寸、16 个应用图标格的大尺寸。值得注意的是:对于小尺寸的桌面小组件来说,除自身显示的信息外,其本质上就仅仅是一个按钮。开发者无法自定义任何小组件中的互动模块,除展示信息外其功能只有一个,就是点击后跳转到哪里去。而中号或者大号应用小组件,则支持稍多一些的互动选项,允许开发者提供多个按钮。


2、系统级别的功能

WidgetKit 是纯 SwiftUI 视图的框架。得益于 SwiftUI 及诸多系统内置的功能,在开发者什么都不做的前提下,小组件自身已经具备了几项常用功能。本小节用「自动暗色模式」和「小组件推荐轮转」举例。

如下图所示,当设备从浅色模式切换至深色模式时,界面中的黄色做出了轻微的版本调整。所有字体的和背景的颜色也进行了反转,比如 Steve's Surprise Party 从黑色切换到了白色。对于这些设置,只要你的视图代码中用到了类似 .color(.primary) 这样的标准写法,或用到了系统提供的标准色,以上特性都是自动白送的。

如下图所示,用户可以将多个小组件叠加在一起,成为一个堆叠 Stack。 对于堆叠在一起的小组件来说,系统会使用基于 On-Device Intelligence 本地智能的机器学习 + 推荐算法,来决定在某个时刻,哪一个小组件会在屏幕的最上方。其数据来源主要是小组件的 Donation 捐赠,举个例子,倘若在厦门的用户在早上 8:30 分的时候查看了天气,那么天气应用很可能捐赠了一个「8:30 查看厦门天气」的事件。当捐赠形成规律后,系统会将这些捐赠当作一个新知识,下次 8:30 分时自动将天气小组件前置。


3、制作桌面小组件的流程

在 Xcode 中新增小组件的流程与新增 watchOS 应用的流程非常相似,都需要新增一个 Target。如下图所示,在顶部的菜单栏中选择「File - New -Target」。

在弹出的选项中,选择「iOS」。接着在「Application Extension」中找到「Widget Extension」并点击下一步。

此处弹出的是小组件在开发期间的名称,根据你的喜好起名即可,不得与项目名重复。本例中,使用 WidgetTarget 作为名称,如下图所示。

完成以上步骤后,Xcode 会新建几份模版文件,其中之前没见过的文件类型是:WidgetTarget.swiftWidgetTarget.intentdefinitions。在什么内容都不添加的情况下,该模版文件已经准备好了一个时钟应用,直接运行后如下图所示,显示当前时间。本文会专注讲解这个时钟应用模版中各类代码的用途。

在上图中,你也许注意到了系统的实际时间是 6:59,而小组件显示时间是 6:56,这是因为小组件的更新不是即时的,而是由系统根据开发者提供的时间轴来尽量安排。


4、代码详解

上述流程走完后,Xcode 模版会自动生成的 WidgetTarget.swift 文件,如下图所示。

此处的 WidgetTarget.swift 文件名,与之前创建 Widget 时空格中填写的命名一致。因每个人的命名可能不同,你的文件可能不叫 WidgetTarget,但内容是一致的,不必担心。本例中,我将以 WidgetTarget 这个命名为例子。

WidgetTarget.swift 文件中包含五个重要的结构体 struct,分别是:ProviderSimpleEntryWidgetTargetEntryViewWidgetTargetWidgetTarget_Previews。这些概念相对较新,大多是 2020 年之后 Apple 发布的 WidgetKit 中提到的全新概念,因此我会在下文乱序进行逐一解析。

如下图所示,WidgetTargetEntryView 结构对应的是标准的 SwiftUI 视图。其中与视图有关的代码写在 var body: some View { } 中。如下图所示,Text(entry.date, style: .time) 指的是用 date 这个时间戳用于数据来源,并将其以 time 时钟的方式显示出来,是一个 Text 纯文本视图。

你也许会好奇,传递数据的信息是从哪里来的,答案是 SimpleEntry 这个结构体。如下图所示,SimpleEntry 继承了一个名为 TimelineEntry 的协议。对于小组件来说,存在一个重要的时间轴 Timeline 的概念 ,对于组件的任何一个时刻,系统需要知道组件想显示什么内容,并根据组件的时间轴进行排期。比如一个日历应用,很可能每天在三个时间点各更新一次,每次对应的时间的信息都不同,每一个时间点的信息片段,都存在 SimpleEntry 中。

如下图所示,WidgetTargetWidgetTarget_Previews 这两个结构体负责视图的显示和 Xcode 窗口预览。其中 WidgetTarget_Previews 就不多说了,只是单纯地提供该小组件的 Canvas 预览窗口,与实际允许在设备中的代码无关。.previewContext(WidgetPreviewContext(family: .systemSmall)) 是个新的修改器,指的是用小尺寸的小组件来预览。

上图中 @main 指的是小组件应用的接入点。其中 WidgetTargetEntryView(entry: entry) 负责把上文中定义的 SwiftUI 视图 WidgetTargetEntryView 代入最新数据并显示。值得额外介绍的是两个新的修改器,此处 .configurationDisplayName("My Widget") 指的是小组件选择界面的组件名称,.description("This is an example widget.") 指的是小组件描述,对应内容如下图所示。

最后值得一提的新内容便是 Provider 这个结构体,如下图所示,继承了 IntentTimelineProvider。顾名思义,Provider 是一个提供者,负责由小组件向操作系统提供信息。这个协议来要求开发者提供三个必备函数,分别是 placeholdergetSnapshotgetTimeline

getPlaceholder 的用途是在没有网络连接,或数据还没能及时加载的情况下,小组件接收到的默认的信息。这一部分信息可以由开发者直接提供,以淘宝为例,货物信息默认可以是「精选商品」,运输信息可以是「已经在路上」。

getSnapshot 则是在诸如小组件选择界面,屏幕上真实展示的小组件中,显示的具备真实信息的内容。举个例子,假设现在是 10:00 分,则 getSnapshot 返回的便是具备 10:00 这个信息的小组件状态。

getTimeline 指的是操作系统向小组件索要的时间轴。系统需要知道桌面显示出来的每个组件,需要在什么时候进行数据更新。比如你的小组件显示的内容只是每日精选书摘,那么时间轴中的内容便是「每日更新一次」这个时间。系统会根据这个频率来对组件内容进行更新。

请注意,在时间轴函数中,开发者给出的时间轴只是告诉系统我希望你按照这个标准刷新小组件。但系统并不保证执行,因此这只是君子协议,实际刷新时间由系统根据当前电量、显示在最上层的组件等信息决定。

上图中的文件 WidgetTarget.intentdefinition,作用是为小组件提供更复杂的自定义功能。这一部分内容对于新人有些超纲了,因为它涉及了一些关于 SiriKit的知识,本教程不做额外扩展讲解。

以上知识可以带你入门,却不足以凭此构建一个复杂的小组件应用。若你对创建复杂桌面小组件感兴趣,强烈推荐以下 WWDC 开发者大会视频。

桌面小组件算是过去数年中,能带来明显用户感知的新系统特性。其核心目的在于展示信息,并非提供深层次互动。你可以围绕该目的,去思考应用中哪些信息适合以小组件的形式呈现出来。与此同时,若你的应用的确需要的是复杂的互动,直接考虑完整应用会更合适一些,切忌把复杂逻辑交给注重「轻量」的小组件。


Demo

Demo在我的Github上,欢迎下载。
SwiftUIDemo

从零开始实操完整的小组件项目
小组件设计
视图构建及自定义

参考文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容