SwiftUI实战-仿写微信App(一)|8月更文挑战

3,153 阅读4分钟

一、目标

模拟微信的布局,使用SwiftUI仿写微信App

image.png

二、思路

image.png

  • 首先

将红色框选的区域,作为导航区(Navigation)。

  • 其次

蓝色框选的区域是内容列表(ContentView)。

  • 最后

绿色框选的区域是页签(TabView)。

实际上,作为App的页签,优先要做的,它是微信通讯录发现四个页面的导航按钮。通过点击TabView上的导航按钮,可以分别跳转到对应的页面上。

三、TabView页签

SwiftUI中,TabView是官方已经定义好的通用组件。

image.png

TabView作为组件的主体,包裹导航按钮和导航对应的页面。

比如文档中的事例中,Text("The First Tab")即为导航对应的页面。

.tabItem中包裹的即为导航按钮。

如下图

image.png

所以,回归主题,通过改动导航按钮得到如下代码:

TabView {
    Text("微信页面")
        .tabItem {
            Image(systemName: "message.fill")
            Text("微信")
        }
    Text("通讯录页面")
        .tabItem {
            Image(systemName: "person.2.fill")
            Text("通讯录")
        }
    Text("发现页面")
        .tabItem {
            Image(systemName: "location.circle")
            Text("发现")
        }
    Text("我页面")
        .tabItem {
            Image(systemName: "person")
            Text("我")
        }
}

效果如下

image.png

请忽略图标不太一致的问题,图标默认使用的是SF Symbols图标库中的图标,这是苹果官方定义的一套图标库,大约有1500多个图标,但由于微信的图标是自设计的图标,所以有些图标在库中并没有,我找了一些类似的。当然可以通过Creating Custom Symbol Images for Your App创建自定义图标,导入Xcode中后,可以像SF Symbols内置的图标一样使用。

四、Navigation导航

设置好页签后,回到文章开头,下一步实现导航区。

SwiftUI中,通过NavigationView可以将包裹住的View组件转换为可导航的(通过NavigationLink的配合,可以实现页面的跳转,此刻暂且不提)。

然后通过设置导航属性,实现目标。代码如下:

NavigationView{
    Text("微信页面")
        .navigationTitle("微信")
        .navigationBarItems(leading: Text("· ·"), trailing: Image(systemName: "plus.circle"))
        .navigationBarTitleDisplayMode(.inline)
}.tabItem {
    Image(systemName: "message.fill")
    Text("微信")
}
  • .navigationTitle设置导航标题
  • .navigationBarItems设置左右导航按钮
  • .navigationBarTitleDisplayMode设置导航区域显示模式,默认为.automatic不显示导航区,只有滑动页面的时候才会复现出来。.inline即为直接显示导航区。

此时我们再看实际效果:

image.png

五、内容布局

列表暂且不提,先看每个列表内容的布局。

首先,将横向的区域分成两块。1是图片,2是内容。

SwiftUI中,通过HStack布局组件,设置横向布局。

模拟代码如下:

HStack{
    Image()
    Content()
}

image.png

其次,拆分内容。纵向分为3块,上方是发布人大众点评,空格Spacer()与日期2021/7/31。中间是最新消息必吃榜发布......,最下方是一个分割线Divider()

image.png

SwiftUI中,通过VStack布局组件,设置纵向布局,再结合HStack

模拟代码如下:

VStack{
    HStack{
        Text("大众点评")
        Spacer()
        Text("2021/7/31")
    }
    Text("必吃榜发布......")
    Divider()
}

再调整下颜色、文本大小、padding等,最终代码如下:

HStack{
    Image("dzdp").resizable().scaledToFill().frame(width: 75, height: 75).cornerRadius(10).clipped().padding()
    VStack(alignment: .leading){
        HStack{
            Text("大众点评").foregroundColor(.blue).font(.title3)
            Spacer()
            Text("2021/7/31").padding().foregroundColor(.gray)
        }
        Text("必吃榜发布!解密本地人爱吃的10碗面").foregroundColor(.gray).font(.callout).bold()
        Divider()
    }
}

预览效果如下:

image.png

这段代码结构,可以被多次复用,所以创建文件MessageView.swift,将该代码迁入进来。

再将图片、发布人、日期、最新消息字段作为参数传入,再来看代码:

import SwiftUI

struct MessageView: View {
    
    
    var imageName: String
    
    var publisher: String
    
    var date: String
    
    var message: String
    
    var body: some View {
        HStack{
            Image(imageName).resizable().scaledToFill().frame(width: 75, height: 75).cornerRadius(10).clipped().padding()
            VStack(alignment: .leading){
                HStack{
                    Text(publisher).foregroundColor(.blue).font(.title3)
                    Spacer()
                    Text(date).padding().foregroundColor(.gray)
                }
                Text(message).foregroundColor(.gray).font(.callout).bold()
                Divider()
            }
        }
    }
}

struct MessageView_Previews: PreviewProvider {
    static var previews: some View {
        MessageView(imageName: "dzdp", publisher: "大众点评", date: "2021/7/31", message: "必吃榜发布!解密本地人爱吃的10碗面")
    }
}

视图View抽象:文中的各个内容页,比如微信通讯录等随着后期内容的扩增,会变得越来越庞大,也难以维护,所以在此,将各个内容页创建对应的SwiftUI文件抽象出来。

image.png

  • ChatListView对应微信
  • MailListView对应通讯录
  • ExploreView对应发现
  • MineView对应

结果如下图:

image.png

六、列表循环ForEach

SwiftUI内置了一个循环组件的迭代器ForEach,与for循环很类似,先简单示范一下:

VStack{
    ForEach(0..<5){index in
        MessageView(imageName: "dzdp", publisher: "大众点评", date: "2021/7/31", message: "必吃榜发布!解密本地人爱吃的10碗面")    
    }
}

这是最简单的一种循环方式,ForEach的入参是一个区间运算符0..<5代表从0开始,每次+1,循环到4为止。

这种计数的循环方式明显不符合要求,因为每个列表的内容都不一样。

所以需要以下2步:

    1. 创建一个Message,包含imageNamepublisherdatemessage4个字段。
    1. 创建一个Messages数组,用于存储不同的Message

所以创建Message.swift,代码如下:

import Foundation

struct Message: Identifiable{
    var id: Int
    var imageName: String
    var publisher: String
    var date: String
    var message: String
}

这里,Message实现Identifiable协议,并且增加了id字段,这是因为ForEach遍历的元素必须满足该条件。

然后创建数组messages

let messages: [Message] = [
    Message(id: 0, imageName: "jd", publisher: "京东JD.COM", date: "2021/7/31", message: "交易完成通知"),
    Message(id: 1, imageName: "hdl", publisher: "海底捞火锅", date: "2021/7/31", message: "这碗胡辣汤,可得劲儿!"),
    Message(id: 2, imageName: "dzdp", publisher: "大众点评", date: "2021/7/31", message: "必吃榜发布!解密本地人爱吃的10碗面")
]

代码以及效果:

image.png

最后启动Simular看最终效果

image.png

附:代码地址

gitee.com/dkwingcn/we…

相关文章

SwiftUI实战-仿写微信App(二)

SwiftUI实战-仿写微信App(三)

SwiftUI实战-仿写微信App(四)