简介

最近在学习 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. docker 概览 (1)

    Docker Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化.容器是完全使 ...

  2. MySQL系列操作

    Linux环境下安装使用MySQL Portal 数据备份&恢复 Portal

  3. 文本文件的合并操作方法 - Python

    我们有时候,看到几k的日志文件,一大堆,一个一个打开又很麻烦,少看几个,又担心遗漏,这个时候,如果有一个可以合并所有文本文件的工具就好了. 下面这个代码就可以实现,它不局限于.txt格式,基本上字符型 ...

  4. 如何防止CDN防护被绕过

    当攻击者发现目标站点存在CDN防护的时候,会尝试通过查找站点的真实IP,从而绕过CDN防护.我们来看一个比较常见的基于公有云的高可用架构,如下:CDN(入口层)->WAF(应用层防护)-> ...

  5. 关于对vue-router的优化(详尽版)

    这两天总结了关于vue-router优化的几点技法,做个笔记 在基于vue的移动端app中,通过vue-router可以便捷的进入某一路由或回退到上一路由,但是若不对vue-router做相关优化处理 ...

  6. pytorch-API实现线性回归

    示例: import torch import torch.nn as nn from torch import optim class MyModel(nn.Module): def __init_ ...

  7. Java 多线程实现方式一:继承Thread类

    java 通过继承Thread类实现多线程很多简单: 只需要重写run方法即可. 比如我们分三个线程去京东下载三张图片: 1.先写个下载类: 注意导入CommonsIO 包 public class ...

  8. Spiking-YOLO : 前沿性研究,脉冲神经网络在目标检测的首次尝试 | AAAI 2020

    论文提出Spiking-YOLO,是脉冲神经网络在目标检测领域的首次成功尝试,实现了与卷积神经网络相当的性能,而能源消耗极低.论文内容新颖,比较前沿,推荐给大家阅读   来源:晓飞的算法工程笔记 公众 ...

  9. php continue 跳出多重循环

    来源参考:https://blog.csdn.net/xyy94813/article/details/50834938 /* * 在PHP中break语句不仅可以跳出当前循环,还可以指定跳出几层循环 ...

  10. Spring5参考指南:AspectJ注解

    文章目录 什么是AspectJ注解 启用AOP 定义Aspect 定义Pointcut 切入点指示符(PCD) 切入点组合 Advice 访问JoinPoint Advice参数 Advice参数和泛 ...