简介

最近在学习 SwiftUI ,我一般都是先去学习界面布局,所以就想着仿写一下经常使用的软件的界面,所以先拿微信开刀。因为不想一次性发太多的内容,所以只好将主题分解,一部分一部分地去讲,接下来我们一起来学习吧。

如果你尝试过使用 SwiftUI 编写界面,你会发现是如此地舒心,我已深深地爱上了它。当然它的坑并不少,毕竟才刚出来,最低支持系统是 iOS13,估计还得等个几年才会慢慢在公司里使用上吧。但是这并不妨碍我们的学习。

在这篇文章里,我会一步一步编写微信的首页列表视图,一步一步将代码呈现上来,并仔细地讲解,我相信你们都可以看懂的,先来看看效果图。

很简单吧?是很简单,但是在编写的时候还是有些技巧在里面,毕竟,简单才不容易劝退嘛。在开始前先讲一下这篇文章将会用到的一些布局与组件,先给大家一个印象,方便后面的阅读理解。

HStack - 水平布局

VStack - 垂直布局

Text - 文本控件

Spacer - 扩展空间,使容器填满布局空间

Image - 图像控件

List - 列表控件

Divider - 分隔线控件

工作环境

Xcode - Version 11.3.1 (11C504)

Swift - version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)

开始编写代码

编写列表行

我们来先把头像添加进来。

Image("1")
.resizable() // 1
.frame(width: 46, height: 46) // 2
.cornerRadius(6)// 3

1 - 在 SwiftUI 中,如果需要控制图像的大小,则必须先调用resizable修饰

2- 设置图像大小

3 - 设置圆角大小,四个角的大小都相同

因为布局是横向的,所以我们在外层使用HStack包裹起来,然后添加联系人名字和最后发消息时间。

HStack {
// 头像 HStack {
Text("女神")
.font(.body) // 1 Spacer() Text("下午 2:55")
.font(.caption)
.foregroundColor(Color.gray.opacity(0.5)) // 2
}
}

1 - 使用 font 修饰字体,这里使用了苹果提供的标准字体,苹果还提供了 largeTitle, title, headline, subheadline, body, callout, footnote, caption。

2 - 使用 foregroundColor 修饰字体颜色,因为 gray 的灰色还是太黑了,所以这里又使用了 opacity 去修饰透明度为50%,使它显得更淡一点。

名字下方显示的是是最后发送或接收的消息内容,因此我们在外层使用 VStack 包裹起来。

VStack(alignment: .leading, spacing: 6) { // 1
// 名称和时间 Text("对不起,你是个好人")
.font(.callout)
.foregroundColor(Color.gray)
}

// 1 - 设定VStack里子控件居左对齐,默认是居中对齐。再设定子控件的间隙为 6 个像素,这样比较符合微信上面的设计。

现在样子已经出来了,我们先预览下效果。

我们给最外层的HStack增加padding,使它更美观一些,参数填写.all代表四周都需要边框。经过我的眼力观察,它的默认是 16px 的样子。

HStack {
// 头像、名称、时间、消息内容
}
.padding(.all)

有了间距,好看多了。

接下来创建一个视图,它负责装载行视图,起名为GCMainRow

struct GCMainRow: View {
var body: some View {
HStack {
Image("1")
.resizable()
.frame(width: 46, height: 46)
.cornerRadius(6) VStack(alignment: .leading, spacing: 6) {
HStack {
Text("女神")
.font(.body) Spacer() Text("下午 2:55")
.font(.caption)
.foregroundColor(Color.gray.opacity(0.5))
}
Text("对不起,你是个好人")
.font(.callout)
.foregroundColor(Color.gray)
}
}
.padding(.all)
}
}

然后在ContentView改为调用GCMainRow(),这样代码就好看很多了。

struct ContentView: View {
var body: some View {
GCMainRow()
}
}

编写列表 List

好了,现在让我们来编写列表视图吧。我们在最外层使用List包裹GCMainRow,循环 20 个视图,数据多点才可以让我们滚动。

List(0 ..< 20) { _ in // 1
GCMainRow()
}

1 - 因为我们不需要用到循环的一些数据,所以我们使用 _ 去忽略它。

List控件默认的都会有边距,下图黄色是GCMainRow的大小,可以看得出来旁边有空白的填充,这对我们当前的设计来说不太友好,因此我们需要想办法去掉这些边距填充。

List提供了listRowInsets来控制行的边距(上下左右),我们来试着使用一下。

List(0 ..< 20) { item in
GCMainRow()
.listRowInsets(EdgeInsets())
}

我们发现,这样写是没有作用的,listRowInsets的生效条件是“不能直接在List中使用,需要配合For Each语句才能生效”,我们再修改一下代码。

List {
ForEach(0 ..< 20) { item in
GCMainRow()
.listRowInsets(EdgeInsets())
}
}

好了,这次生效了,这就是我们要的结果。

自定义分隔线

我们仔细观察一下分隔线,在微信里分隔线是左对齐在名称和消息内容的,所以我们需要把现有的分隔线隐藏掉,然后再实现它。

在这里讲解一下,List是基于UITableView去实现的,这意味着我们可以通过appearance全局修改它的所有属性,正如我们现在需要取消它默认的分隔线,将separatorStyle设置为.none即可。

init() {
UITableView.appearance().separatorStyle = .none
}

因为分隔线是贴着右边缘的,所以我们需要在包裹着名称、时间、消息内容的VStack外层再包裹一层VStack,在其中再添加分隔线Divider,并将里层的VStack设定右边距,最后将最外层的HStackpadding改为上和左边距。是不是听得有点懵?没关系,看看代码就很容易理解了。

HStack(alignment: .top) { // edit
// 头像
VStack { // new
VStack(alignment: .leading, spacing: 6) {
// 名称、时间、消息内容
}
.padding(.trailing) // new
Divider() // new
}
}
.padding(.top) // edit
.padding(.leading) // edit

我们现在来看一下效果。

未读消息小红点

未读消息在头像的右上方,小红点的中心点是位于头像的右上顶端。我们可以使用overlay叠加一个视图,来制作小红点吧。

Image("1")
// ...
.overlay(
Color.red // 1
.frame(width: 16, height: 16)
.cornerRadius(8)
.offset(x: 23, y: -23) // 2
)

// 1 - Color 本身也是一个视图组件,这是官方的定义 @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) extension Color : View { }

// 2 - 设定视图的偏移量,那么23是怎样得出来的呢?很简单,因为默认的overlay视图是位于父视图的中心,那么我们要将它放置在右上角,那么只需要宽高都除以2就可以了,那么这里的结果就是,x轴增加23px,y轴减少23px

接下来是未读数量,和上面的相似,在Color视图上利用overlay叠加Text视图就可以了。

Color.red
.overlay( // 1
Text("1")
.font(.caption)
.foregroundColor(.white)
)
.frame(width: 16, height: 16)
.cornerRadius(8)
.offset(x: 23, y: -23)

// 1- 值得注意的是,因为文本也是需要偏移到右上角的,所以必须放在前面,不然它默认就是居中的

至此,代码演示结束,预览一下静态图,文章开头有 gif 动态图效果。

总结

好了,这篇文章就到这里,篇幅有点长了。不过没关系,我们来总结一下关键点:

  1. List默认是有行边距的,要取消或修改它的行边距,我们必须通过For Each再配合上listRowInsets才能实现。
  2. List是基于UITableView去实现的,这意味着我们可以通过appearance全局修改它的所有属性。
  3. 使用overlay可以给视图叠加一个视图。

Demo 源码下载

我已经把 Demo 上传至 GitHub 上面,项目名字是 SwiftUI-Tutorials,目录名为GCWechatList,有需要的朋友可以去下载运行一下,当然你也可以跟着文章去做一遍,这样更有利于你掌握此方面的知识。

文章篇幅有点长,虽然教的东西也挺简单,但概述得比较详细。任何东西都是先从简单入手的,才不会造成劝退不是吗?哈哈,此文章针对于新手而言还是很友好的,对于已经会的人来讲就可能废话有点多了,如果必须要喷,请轻喷,我比较玻璃心。

如果本文章对你有帮助,请关注我,你的关注就是我后续写文章的动力,下期会更精彩噢!

关于作者

博文作者:GarveyCalvin

微博:https://weibo.com/feiyueharia

博客园:https://www.cnblogs.com/GarveyCalvin

本文版权归作者,欢迎转载,但必须保留此段声明,并给出原文链接,谢谢合作!

公众号

作者第一次运营公众号,请你们一定要关注我的公众号,给我点动力,后期主要运营公众号为主。这是第二篇发布的文章,需要你们的支持,谢谢你们!

QQ群

一起讨论 SwiftUI,群主喜欢看热闹,当吃瓜人员。进来时填写你在哪里看到此文章的,并介绍下自己,一句话就行。

SwiftUI - 一起来仿写微信APP之一首页列表视图的更多相关文章

  1. 安卓开发笔记(三十三):Android仿写微信发现

    首先我们来看看仿写之后的效果: 看到是这个界面我们首先应该思考这些按钮是怎么做出来的?有了一个整体的思路之后才知道该怎么办.最开始我想的就直接利用button控件上面直接加上png的图片就可以形成一个 ...

  2. vue 仿写微信公众号自定义菜单

    先看效果图 代码参考 <template> <div> <!-- 公众号设置 --> <el-col :span="24" style=& ...

  3. .Net语言 APP开发平台——Smobiler学习日志:用MenuView控件仿钉钉APP的首页菜单

    最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 一.目标样式 我们要实现上图中的效果,需要如下的操作: 1.从工具栏上的”Smobil ...

  4. 用weexplus从0到1写一个app

    说明 基于wexplus开发app是来新公司才接触的,之前只是用过weex体验过写demo,当时就被用vue技术栈来开发app的开发体验惊艳到了,这个开发体验比react native要好很多,对于我 ...

  5. 用weexplus从0到1写一个app(2)-页面跳转和文章列表及文章详情的编写

    说明 结束连续几天的加班,最近的项目终于告一段落,今天抽点时间开始继续写我这篇拖了很久的<用weexplus从0到1写一个app>系列文章.写这篇文章的时候,weexplus的作者已经把w ...

  6. Vue仿微信app页面跳转动画

    10:14:11独立开发者在开发移动端产品时,为了更高效,通常会使用Web技术来开发移动端项目,可以同时适配Android.iOS.H5,稍加改动还可适配微信小程序. 在使用Vue.js开发移动端页面 ...

  7. 【手把手教程】uniapp + vue 从0搭建仿微信App聊天应用:腾讯云TXIM即时通讯的最佳实践

    基于uniapp + vue 实现仿微信App聊天应用实践,实现以下功能 1: 用户登陆 2: 聊天会话管理 3: 文本/图片/视频/定位消息收发 4: 贴图表情消息收发 5: 一对一语音视频在线通话 ...

  8. Android中使用ExpandableListView实现微信通讯录界面(完善仿微信APP)

    之前的博文<Android中使用ExpandableListView实现好友分组>我简单介绍了使用ExpandableListView实现简单的好友分组功能,今天我们针对之前的所做的仿微信 ...

  9. Vue2 全家桶仿 微信App 项目,支持多人在线聊天和机器人聊天

    前言 这个项目是利用工作之余写的一个模仿微信app的单页面应用,整个项目包含27个页面,涉及实时群聊,机器人聊天,同学录,朋友圈等等,后续页面还是开发中.写这个项目主要目的是练习和熟悉vue和vuex ...

随机推荐

  1. day8作业

    # 一:for循环 # 1.1 for循环嵌套之打印99乘法表 for i in range(1,10): for j in range(1,i+1): print("{} * {} = { ...

  2. stand up meeting 12-10

    今天项目会议正好利用了大家上课前的十五分钟,大家对项目进度和项目中所遇到的问题进行了沟通. 由于天赋同学与重阳小组沟通及时有效,在mapping的过程中直接将单词本中的type与我们单词挑战中的que ...

  3. D. 蚂蚁平面

    D. 蚂蚁平面 单点时限: 2.0 sec 内存限制: 512 MB 平面上有 n只蚂蚁,它走过的路径可以看作一条直线 由这n 条直线定义的某些区域是无界的,而另一些区域则是有界的. 有界区域的最大个 ...

  4. SpringBoot集成Shiro实现权限控制

    Shiro简介 Apache Shiro是一个功能强大且易于使用的Java安全框架,用于执行身份验证,授权,加密和会话管理.使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移 ...

  5. [Abp vNext 入坑分享] - 3.简单的用户模块功能开发

    一.简要说明 本篇文章开始进行业务模块的开发模拟,借助user模块来进行业务开发,主要是用户相关的基础操作.主要是先使用Users来体验整个开发的流程.主要是先把一个基础流程跑顺利,在这里我并不会过于 ...

  6. GraphicsLab Project 之 Curl Noise

    作者:i_dovelemon 日期:2020-04-25 主题:Perlin Noise, Curl Noise, Finite Difference Method 引言 最近在研究流体效果相关的模拟 ...

  7. 高级数据结构---赫(哈)夫曼树及java代码实现

    我们经常会用到文件压缩,压缩之后文件会变小,便于传输,使用的时候又将其解压出来.为什么压缩之后会变小,而且压缩和解压也不会出错.赫夫曼编码和赫夫曼树了解一下. 赫夫曼树: 它是一种的叶子结点带有权重的 ...

  8. Synchronization and Overlapped Input and Output

    You can perform either synchronous or asynchronous (also called overlapped) I/O operations on files, ...

  9. Qt 的日期 时间

    QDateTime 的构造函数,有参数是QDate的.这样就可以把日期转化成 QDateTime. QDateTime.toTime_t() 可以转化成 Unix 时间.

  10. 小白也能轻松上手的Prometheus教程

    这篇文章将承接此前关于使用Prometheus配置自定义告警规则的文章.在本文中,我们将demo安装Prometheus的过程以及配置Alertmanager,使其能够在触发告警时能发送邮件,但我们将 ...