剖析:如何用 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集装箱,车辆定位,叉车定位可视化孪生系统——第十五课
序 又是快两个月没写随笔了,长时间不总结项目,不锻炼文笔,一开篇,多少都会有些生疏,不知道如何开篇,如何写下去.有点江郎才尽,黔驴技穷的感觉. 写随笔,通常三步走,第一步,搭建框架,先把你要写的内容框 ...
随机推荐
- Tengine MLOps概述
Tengine MLOps概述 大幅提高产业应用从云向边缘迁移的效率 MLOps Cloud Native 聚焦于提升云端的运营过程效率 MLOps Edge Native 聚焦于解决边缘应用开发及异 ...
- 广播 (broadcasting)
广播 (broadcasting) 飞桨(PaddlePaddle,以下简称Paddle)和其他框架一样,提供的一些API支持广播(broadcasting)机制,允许在一些运算时使用不同形状的张量. ...
- 用NVIDIA Tensor Cores和TensorFlow 2加速医学图像分割
用NVIDIA Tensor Cores和TensorFlow 2加速医学图像分割 Accelerating Medical Image Segmentation with NVIDIA Tensor ...
- BlazorCharts 原生图表库的建设历程
作者:陈超超 Ant Design Blazor 项目贡献者,拥有十多年从业经验,长期基于.Net技术栈进行架构与开发产品的工作,现就职于正泰集团. 邮箱:timchen@live.com 欢迎各位读 ...
- SpringCloud Alibaba实战(4:基本开发框架搭建)
在上一节,我们已经完成了项目的整体技术架构设计和具体的数据库设计,接下来,我们搭建整体的开发框架. 开发工具选用Idea. 开发工具只是为了提高效率,如果不习惯Idea的话,STS使用起来也是OK的. ...
- 10个有趣又能编译为JavaScript的语言,你用过哪些?
现代应用相比普通的网页有不同的要求.但是浏览器是一个有着一套(大部分)固定可用的技术的平台,JavaScript依然是web应用的核心语言:任何需要在浏览器上跑的应用都需要使用这种语言. 我们都知道J ...
- Java @FunctionalInterface注解-6
在学习 Lambda 表达式时,我们提到如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口.@FunctionalInterface 就是用来指定某 ...
- 重磅!GitHub官方开源新命令行工具
近日,GitHub 发布命令列工具 (Beta) 测试版,官方表示,GitHub CLI提供了一种更简单.更无缝的方法来使用Github.这个命令行工具叫做GitHub CLI,别名gh. 现在,你就 ...
- OO unit1 summary
Unit 1 summary 一.前言 三周左右的学习,OO第一单元顺利结束了,个人认为有必要写个blog来反思总结一下自己第一单元的学习情况,以便更好地进行后面的学习. 之前从来没有写blog的习惯 ...
- 如何利用Python计算景观指数AI
可使用工具包 pylandstats 此工具包基本是根据fragstats形成的,大部分fragstats里面的景观指数,这里都可以计算.但是,还是有一小部分指数这里没有涉及. LS_METRICS ...