剖析:如何用 SwiftUI 5天组装一个微信 —— 通讯录发现我篇
前置资源
GitHub: SwiftUI-WeChatDemo
第一章:剖析:如何用 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天组装一个微信 —— 通讯录发现我篇的更多相关文章
- 用 SwiftUI 五天组装一个微信
GitHub 链接:SwiftUI-WeChatDemo 效果图 实装内容 4 个 Tab 页面 + 聊天界面,使用纯 SwiftUI 搭建而成 应用启动界面 Launch Screen 国际化及应用 ...
- 剖析:如何用 SwitchUI 5天写一个微信 —— 聊天界面篇
前置资源 GitHub: SwiftUI-WeChatDemo 第零章:用 SwiftUI 五天组装一个微信 - wavky - 博客园 整体结构 UI 部分代码分布如上图所示,App 的主入口类为 ...
- 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(3)
[编者按]本篇文章作者是 Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于 Parse 特点,打造一款类似 Instagr ...
- 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?(2)
[编者按]本篇文章作者是 Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于 Parse 特点,打造一款类似 Instagr ...
- 如何用 Parse 和 Swift 搭建一个像 Instagram 那样的应用?
[编者按]本篇文章作者是Reinder de Vries,既是一名企业家,也是优秀的程序员,发表多篇应用程序的博客.本篇文章中,作者主要介绍了如何基于Parse特点,打造一款类似Instagram的应 ...
- 如何用DOS命令,获取一个目录下的文件数目
发信人: GOOGOODALLS (我爱Figo), 信区: DOS 标 题: 如何用DOS命令,获取一个目录下的文件数目? 发信站: 水木社区 (Fri Mar 9 08:40:01 2007) ...
- 如何用webgl(three.js)搭建一个3D库房,3D密集架,3D档案室(升级版)
很长一段时间没有写3D库房,3D密集架相关的效果文章了,刚好最近有相关项目落地,索性总结一下 与之前我写的3D库房密集架文章<如何用webgl(three.js)搭建一个3D库房,3D密集架,3 ...
- 如何用webgl(three.js)搭建一个3D库房,3D仓库,3D码头,3D集装箱可视化孪生系统——第十五课
序 又是快两个月没写随笔了,长时间不总结项目,不锻炼文笔,一开篇,多少都会有些生疏,不知道如何开篇,如何写下去.有点江郎才尽,黔驴技穷的感觉. 写随笔,通常三步走,第一步,搭建框架,先把你要写的内容框 ...
- 如何用webgl(three.js)搭建一个3D库房,3D仓库3D码头,3D集装箱,车辆定位,叉车定位可视化孪生系统——第十五课
序 又是快两个月没写随笔了,长时间不总结项目,不锻炼文笔,一开篇,多少都会有些生疏,不知道如何开篇,如何写下去.有点江郎才尽,黔驴技穷的感觉. 写随笔,通常三步走,第一步,搭建框架,先把你要写的内容框 ...
随机推荐
- 5G通讯与芯片
5G通讯与芯片 美国商务部可能接近达成一项新的规则,允许美国公司与华为重启谈判,在共同制定下一代通信技术5G标准方面进行合作. 华为美国首席安全官安迪·珀迪(Andy Purdy)向第一财经记者独家回 ...
- TensorFlow实现多层感知机函数逼近
TensorFlow实现多层感知机函数逼近 准备工作 对于函数逼近,这里的损失函数是 MSE.输入应该归一化,隐藏层是 ReLU,输出层最好是 Sigmoid. 下面是如何使用 MLP 进行函数逼近的 ...
- 循环IRNNv2Layer实现
循环IRNNv2Layer实现 IRNNv2Layer实现循环层,例如循环神经网络(RNN),门控循环单元(GRU)和长期短期记忆(LSTM).支持的类型为RNN,GRU和LSTM.它执行循环操作,该 ...
- .NET平台系列26:在 Windows 上安装 .NET Core/.NET5/.NET6
系列目录 [已更新最新开发文章,点击查看详细] 本文介绍如何在 Windows 上安装 .NET. .NET 由运行时和 SDK 组成. 运行时用于运行 .NET 应用,应用可能包含也可能不包 ...
- Android adb的常用命令
环境部署: 1.下载adb工具 2.下载奇兔刷机(或其它一键刷机软件),将手机与电脑进行连接 3.一键root手机 命令 1.获取设备列表及设备状态:adb devices 如果连接的设备不止一个, ...
- 量子:基于ERP块对的两步量子直接通信
学习论文: 题目:Two-step quantum direct communication protocol using the Einstein-Podolsky-Rosen pair block ...
- 【NX二次开发】Block UI 通过浏览选择文件
属性说明 属性 类型 描述 常规 BlockID String 控件ID Enable Logical 是否可操作 Group ...
- 【NX二次开发】批量数字签名的方法,解决自己电脑编译的dll在用户正版NX无法使用的问题
在UG5.0开始,所有开发的DLL都要"签名"后才能被客户端上正版的NX调用. 1. 如果是基于c++开发的dll,使用如下方法可以顺利签名成功(这里借用网上现有的文字和图片) 1 ...
- java笔试题(二)
1.写出一维数组初始化的两种方式 int[] arr={1,2,3}; String[] str=new String[2]; str[1]="23"; 2.写出二维数组初始化的两 ...
- spring + spring mvc + tomcat 面试题(史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

