前置资源

GitHub: SwiftUI-WeChatDemo

第零章:用 SwiftUI 5天组装一个微信

第一章:剖析:如何用 SwiftUI 5天组装一个微信 —— 聊天界面篇

通讯录

通讯录的数据主体是联系人 Contact,包含姓名和头像,通过自定义函数,将联系人 Contact 数组整理为按名字首字符为分组名的 联系人分组(ContactGroup) 数组:

即:[{"A", [Contact]}, {"B", ...}, ...]

private var contactGroups: [ContactGroup] {
getDefaultContactList()
.group(withLocale: locale)
.sortedList()
} private struct ContactGroup: Hashable {
let groupKey: Character
let groupValue: [Contact]
}

※ 函数实现参考 GitHub源码

由于该界面没有实现子页面跳转功能,因此其实可以不使用 NavigationView,这里使用它只是为了获得 Toolbar 和顶部 Title 显示。

列表视图使用了 List,因为 List 自带了 Section 分组功能,在这里使用上比较方便。

List {
ForEach(contactGroups, id: \.self) { group in
// Completions will crash if ForEach nest together
ContactSection(contactGroup: group)
}
}
.listStyle(PlainListStyle())

通过 ForEach 读取每一个联系人分组,生成自定义的 Section 视图:

视图结构相对简单,但是在生成联系人(List item)视图上,使用了两个 ForEach 进行嵌套(第一层为根据分组 group 数组生成若干 Section,第二层为在每个 Section 中根据该 group 分组中每一条联系人记录生成列表的 Item 视图),当前在 Xcode 12.5.1 上使用多个 ForEach 嵌套时,会发生代码自动提示功能崩坏问题,因此这里将 Section 部分独立出来。


发现

发现页中列表每一项都是固定内容和固定顺序,因此不使用 ForEach 而是手动排布各项元素视图,包括用于灰色背景间隔的 Spacer 在内,总数量超过 10 个 View,因此需要引入 Group 来打包相邻的几个视图项。

数据部分:

private let items = getDiscoverItems()

items 是一个 [DiscoverItemName : ItemBarInfo] 字典,其中 DiscoverItemName 只是一个用作字典 Key 标记便于索引调用的简单枚举,视图所用数据都打包在 ItemBarInfo 中:

enum DiscoverItemName {
case Moments, Channels, Live, Scan,
Shake, TopStories, Search, Nearby,
MiniPrograms
} struct ItemBarInfo {
let icon: String
let iconPattern: IconPattern?
let title: LocalizedStringKey
let name: LocalizedString?
let profileImage: String?
}
  • icon 表示图标使用的素材名称
  • iconPattern 为一个自定义枚举类型,用于封装图标染色所用的颜色或渐变色对象(在下文中详述)
  • title 表示 Item 左侧的名字
  • name 与 profileImage 仅用于在该 Item 需要展示某个联系人的场合(参考上图)

然后通过自定义函数,将每一条 Item 数据转换为对应的 ItemView:

items[.Moments]?.toItemBarView()

extension ItemBarInfo {
func toItemBarView(withDivider: Bool = false,
withBudge: Bool = true) -> ItemBarView {
ItemBarView(itemBarInfo: self,
withDivider: withDivider,
withBudge: withBudge)
}
}

在这个 ItemView 上,需要重点关注的是 Item 左侧的图标,即 Icon:

Icon(image: itemBarInfo.icon,
pattern: itemBarInfo.iconPattern)

这个自定义 Icon 接受两个参数:图标的素材名称,和对图标进行色彩渲染所用的 IconPattern:

在发现页上,朋友圈一栏所用图标为多种色彩,而其他项目的图标则都使用了单一颜色,在实现上,图片素材采用了 SVG 矢量图,再针对性后期进行颜色渲染加工的思路。(朋友圈图标使用渐变色 AngularGradient,而其他图标则使用 Color)

由于颜色 Color 与 一众渐变色类型缺乏公共父类、协议,因此使用自定义封装来实现统一:

对图标素材进行渲染加工,使用 mask 遮罩处理:

color.mask(Image(image))
gradient.mask(Image(image))

该函数工作原理是,对于 Image 中像素所在位置,都使用前者(color 或者 gradient)进行像素对等替换。

Image 原图 mask 渲染后

这一页的各项 Item,沿用发现页的资源,只需针对性提供各项 Icon 资源、Title 指定的数据即可。

顶部的一大片资料区,只需要通过 HStack、VStack 对图片 Image、文字 Text 进行堆砌摆放,局部使用 offset 偏移修正位置即可。

重点关注中间的语言切换按钮。

切换按钮使用 Picker 实现,通过 pickerStyle 调整至该样式。

Picker 初始化中传入到形参 selection 的变量 appLanguage 使用了 @AppStorage 声明,类型为 String,表示该变量会被持久化存储在 UserDefaults 中,并且在未获取到相应字段值时,使用默认值 Language.English.code 进行初始化:

@AppStorage("AppLanguage")
private var appLanguage: String = Language.English.code

※ 与 @State 一样,该变量会绑定到 UI,当数据发生变化时,相关联的 UI 会进行自动刷新重绘

而每个按钮则是通过 ForEach 遍历 Language 枚举实现,当用户点击其中某个按钮时,在 ForEach 中传递至按钮中的 id 将会被赋值到 selection 的变量(即 appLanguage),并被持久化到 UserDefaults 内:

ForEach(languages, id: \.code) { lang in
Text(lang.rawValue)
}

※ ForEach 与 id 的使用参考第一章:剖析:如何用 SwiftUI 5天写一个微信 —— 聊天界面篇

Language 枚举实现中,关键部分为 code,该变量所取用的语言代码需要与项目内的国际化资源文件夹 .lproj 的名称相同,可用语言代码可通过搜索 iOS + Language code 等关键词获得,如:iOS Supported Language Codes (ISO-639)

最后被标记为用户所选择的语言,存储到 UserDefaults 中的 AppLanguage,需要回到 App 应用的主入口,同样通过 @AppStorage 获取出来,并通过 .environment() 写入到整个应用的根 View 的环境变量中,沿 View 结构树传递至每一个 SwiftUI 部件,选择性获取对应语言版本的文字资源:

至于在各个 Text 中使用国际化文本资源的方法,则是通过 SwiftGen 辅助完成的,SwiftGen 的一键配置、使用方法可参考:SwiftGen 在 Swift5 + SwiftUI 下的配置方案

至此,应用内语言切换功能实装完毕。

剖析:如何用 SwiftUI 5天组装一个微信 —— 通讯录发现我篇的更多相关文章

  1. 用 SwiftUI 五天组装一个微信

    GitHub 链接:SwiftUI-WeChatDemo 效果图 实装内容 4 个 Tab 页面 + 聊天界面,使用纯 SwiftUI 搭建而成 应用启动界面 Launch Screen 国际化及应用 ...

  2. 剖析:如何用 SwitchUI 5天写一个微信 —— 聊天界面篇

    前置资源 GitHub: SwiftUI-WeChatDemo 第零章:用 SwiftUI 五天组装一个微信 - wavky - 博客园 整体结构 UI 部分代码分布如上图所示,App 的主入口类为 ...

  3. 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(3)

    [编者按]本篇文章作者是 Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于 Parse 特点,打造一款类似 Instagr ...

  4. 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(2)

    [编者按]本篇文章作者是 Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于 Parse 特点,打造一款类似 Instagr ...

  5. 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?

    [编者按]本篇文章作者是Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于Parse特点,打造一款类似Instagram的应 ...

  6. 如何用DOS命令,获取一个目录下的文件数目

    发信人: GOOGOODALLS (我爱Figo), 信区: DOS 标  题: 如何用DOS命令,获取一个目录下的文件数目? 发信站: 水木社区 (Fri Mar  9 08:40:01 2007) ...

  7. 如何用webgl(three.js)搭建一个3D库房,3D密集架,3D档案室(升级版)

    很长一段时间没有写3D库房,3D密集架相关的效果文章了,刚好最近有相关项目落地,索性总结一下 与之前我写的3D库房密集架文章<如何用webgl(three.js)搭建一个3D库房,3D密集架,3 ...

  8. 如何用webgl(three.js)搭建一个3D库房,3D仓库,3D码头,3D集装箱可视化孪生系统——第十五课

    序 又是快两个月没写随笔了,长时间不总结项目,不锻炼文笔,一开篇,多少都会有些生疏,不知道如何开篇,如何写下去.有点江郎才尽,黔驴技穷的感觉. 写随笔,通常三步走,第一步,搭建框架,先把你要写的内容框 ...

  9. 如何用webgl(three.js)搭建一个3D库房,3D仓库3D码头,3D集装箱,车辆定位,叉车定位可视化孪生系统——第十五课

    序 又是快两个月没写随笔了,长时间不总结项目,不锻炼文笔,一开篇,多少都会有些生疏,不知道如何开篇,如何写下去.有点江郎才尽,黔驴技穷的感觉. 写随笔,通常三步走,第一步,搭建框架,先把你要写的内容框 ...

随机推荐

  1. 元素定位工具ChroPath - Chrome浏览器插件

    一 ChroPath的作用 可以自动识别元素定位表达式,对于系统需要定位元素多时,可使用这种方法减轻定位工作量,但需要验证 二 ChroPath的安装 下载ChroPath -> 在谷歌浏览器访 ...

  2. 将TVM集成到PyTorch

    将TVM集成到PyTorch 随着TVM不断展示出对深度学习执行效率的改进,很明显PyTorch将从直接利用编译器堆栈中受益.PyTorch的主要宗旨是提供无缝且强大的集成,而这不会妨碍用户.PyTo ...

  3. CUDA运行时 Runtime(三)

    CUDA运行时 Runtime(三) 一.异步并发执行 CUDA将以下操作公开为可以彼此并发操作的独立任务: 主机计算: 设备计算: 从主机到设备的内存传输: 从设备到主机的存储器传输: 在给定设备的 ...

  4. Netty 框架学习 —— 传输

    概述 流经网络的数据总是具有相同的类型:字节,这些字节如何传输主要取决于我们所说的网络传输.用户并不关心传输的细节,只在乎字节是否被可靠地发送和接收 如果使用 Java 网络编程,你会发现,某些时候当 ...

  5. 『居善地』接口测试 — 11、接口签名sign原理

    目录 1.什么是加密以及解密? 2.加密方式的分类 (1)对称加密 (2)非对称加密 (3)总结: 3.接口签名sign原理 (1)什么是接口签名? (2)为什么需要做接口签名 (3)接口签名的实践方 ...

  6. JVM系列(五):gc实现概要01

    java的一大核心特性,即是自动内存回收.这让一些人从繁琐的内存管理中解脱出来,但对大部分人来说,貌似这太理所当然了.因为现在市场上的语言,几乎都已经没有了还需要自己去管理内存这事.大家似乎都以为,语 ...

  7. teprunner重磅更新Git打通PyCharm与测试平台

    经过Python测试交流群的小伙伴群策群力,teprunner添加了一个重要功能,把PyCharm中的代码,通过Git同步到测试平台中,生成测试用例.这样,teprunner就成了一个名副其实的pyt ...

  8. 【NX二次开发】调整视图大小

    调整视图大小 tag_t tagView; UF_VIEW_ask_work_view(&tagView); UF_VIEW_fit_view(tagView, 0.7);//if NULL_ ...

  9. Mybatis 中经典的 9 种设计模式!面试可以吹牛了

    虽然我们都知道有23个设计模式,但是大多停留在概念层面,真实开发中很少遇到.Mybatis源码中使用了大量的设计模式,阅读源码并观察设计模式在其中的应用,能够更深入的理解设计模式. Mybatis至少 ...

  10. NOIP模拟测试3「序列·熟练剖分·建造游乐园(play)」

    ---恢复内容开始--- 序列 刚调出来样例就A了,假装是水题. 因为是乱序,我们要求出来每两项之间最小公比,而不是直接比 求出来每两项之间最小公比,然后扫一遍就完了.(还要注意重复情况) 那么问题就 ...