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

2,092 阅读2分钟

前文

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

一、目标

本节我们要实现聊天窗口页面。

image.png

二、思路

image.png

  • 导航区。通过点击导航区左上角的按钮<回到列表页。同理,通过点击列表页的某个列表元素能够跳转到对应的窗口页。

  • 内容区域。聊天气泡+用户图标,在通过循环输出。

  • 工具栏,4个图标按钮+输入框。

三、NavigationLink页面跳转

首先回到上节说到的MessageViewSwiftUI通过NagivationLink组件可以实现从页面A跳转到页面B的效果。

NavigationLink(
    destination: Text("Destination"),
    isActive: $MessageChatPresented,
    label: {
        MessageView(imageName: message.imageName, publisher: message.publisher, date: message.date, message: message.message)
    })
  • label指向的是MessageView即页面A。
  • destination指向的是即将跳转的页面B。
  • isActive是一个Bool值,通过isActive=true实现跳转,反之isActive=false实现返回的效果。这里通过@State注解修饰MessageChatPresented变量,使其从值传递编程引用传递进而可以在父子组件间传递,在子组件修改它的值,父组件也会跟着改变。$符号修饰MessageChatPresented变量,使其转换成Binding,这的作用是是通知子组件,这个变量是引用传递,所以子组件接收时也需用@Binding注解修饰。这种用法即为双向绑定

实现效果如下:

202108022121.gif

NavigationLink默认会指定给destination一个返回按钮<,如果想要自定义按钮并实现返回效果,可以通过修改isActive对应的MessageChatPresented变量的值为false即可。

同理,单独创建一个MessageChatView.swift文件用来编写聊天窗口。然后修改destination: MessageChatView()

再看导航窗口,差一个标题和右上角的按钮。

image.png

同上节,使用NagivationTitlenavigationBarItems即可。

代码如下:

Text("Hello, World!")
    .navigationTitle(title)
    .navigationBarItems(trailing: Image(systemName: "person"))

四、Path聊天气泡

现在需要绘制聊天内容的主题,也就是聊天气泡。气泡的左右有一个小小的三角,是个不规则的形状,所以现有的Shape组件无法满足条件,需要自行绘制。

SwiftUI提供Shape协议,实现该协议并完成path方法即可绘制自定义的图形。

struct ChatBubbleShape: Shape {
    
    func path(in rect: CGRect) -> Path {
        let width = rect.width
        let height = rect.height
        
        let path = Path{p in
            
            p.move(to: CGPoint(x: 0, y: 0)) //移动到坐标轴0,0,默认为左上角
            
            p.addLine(to: CGPoint(x: width, y: 0)) //向右移动width距离
            
            p.addLine(to: CGPoint(x: width, y: height/3)) //向下移动到height/3处
            
            p.addLine(to: CGPoint(x: width+20, y: height/2)) //向右移动20,向下移动到height/2处
            
            p.addLine(to: CGPoint(x: width, y: 2*height/3)) //向左移动20,向下移动到2*height/3处
            
            p.addLine(to: CGPoint(x: width, y: height)) //向下移动到height处
            
            p.addLine(to: CGPoint(x: 0, y: height)) //向左移动到x=0
            
            p.addLine(to: CGPoint(x: 0, y: 0)) //向上移动到x=0,y=0
            
        }
        
        return path
    }
}

绘图顺序如下图所示:

202108022121-2.gif

调整下样式,最终效果如下:

image.png

最后的工作也很简单和重复,在此不一一列举,只是说明下实现方式。

  • 聊天气泡分为左右2种:参考上方的绘图思路。
  • 气泡宽度:可以根据字符的长度动态调整。
  • 圆角问题:结合p.addCurve即可。参考示例

最后使用ScrollView可以实现滚动视图,通过对消息内容的遍历即可实现效果。

image.png

五、工具栏与TextField

工具栏菜单由4个按钮和一个文本域组成。

HStack{
    Image(systemName: "text.bubble").font(.title)
    Image(systemName: "waveform.circle").font(.title)

    TextField("输入", text: $text).textFieldStyle(RoundedBorderTextFieldStyle())

    Image(systemName: "face.smiling").font(.title)

    Image(systemName: "plus.circle").font(.title)
}.padding()

TextField最少包含2个参数,第一个参数为placeholder。第二个参数为输入框的值,需要使用@State$修饰的方式,实现父子间的双向绑定,这样一旦用户通过键盘输入任意值,子组件TextField感知到后就会修改这个值使得父组件感知。

最后通过ZStack加上Rectangle的方式,使工具栏不透明,代码如下:

ZStack(alignment: .bottomLeading){
    Rectangle().fill().foregroundColor(.white)
    HStack{
        
        Image(systemName: "text.bubble").font(.title)
        Image(systemName: "waveform.circle").font(.title)
                        
        TextField("", text: $text).textFieldStyle(RoundedBorderTextFieldStyle())
                        
        Image(systemName: "face.smiling").font(.title)
        
        Image(systemName: "plus.circle").font(.title)
    }.padding()
}.frame(height: 50)

最终效果:

image.png

附:代码地址

gitee.com/dkwingcn/we…

相关文章

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

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