zoukankan      html  css  js  c++  java
  • 剖析:如何用 SwiftUI 5天组装一个微信 —— 通讯录发现我篇

    前置资源

    GitHub: SwiftUI-WeChatDemo

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

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

    效果图


    Tab 2:通讯录

    通讯录的数据主体是联系人 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 部分独立出来。


    Tab 3:发现

    发现页中列表每一项都是固定内容和固定顺序,因此不使用 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 渲染后

    Tab 4:我

    这一页的各项 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 下的配置方案

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

  • 相关阅读:
    【docker】命令学习
    docker 安装mysql
    Docker DockerFile案例 自定义的tomcat9
    尚硅谷 Docker DockerFile案例 ONBUILD命令案例
    dockerfile 案例2 CMD ENTRYPOINT命令案例
    Dockerfile案例
    Dockerfile解析
    数据卷容器
    Dockfile添加数据卷
    容器数据卷
  • 原文地址:https://www.cnblogs.com/wavky/p/SwiftUI-WeChatDemo-Coding-Others.html
Copyright © 2011-2022 走看看