实战编程·使用SwiftUI从0到1完成一款iOS笔记App(二)

4,180 阅读5分钟

前提回顾

在上一章节中,我们从需求分析出发,经历产品原型设计、产品UI设计,到笔记App首页的实战编程开发,走出了从0到1完成一款iOS笔记应用的第一步。

下面,我们将继续完成笔记App的其他部分内容。

笔记App按照页面来划分,可以分为首页、新建笔记页面,在上一章我们完成了首页的编程,这一章节我们来看看“新建笔记”页面。如下图所示:

我们可以看到“新建笔记”页面和“首页”是层叠关系,即“新建笔记”页面是在首页的基础上,由下而上展开的弹窗。这种产品设计方式在之前的章节我们提及过,是一种中度提醒操作,用户的主观感受还放在首页上,基于首页编辑内容的一种交互形式。

在SwiftUI编程中,此类弹窗统称为ModelView模态弹窗。

模态弹窗

模态弹窗,是一种以临时出现的形式显示内容,需要明确的操作才能退出的弹窗。其主要目的有2个,一是帮助用户专注于一个独立任务或一组相关选项。二是确保用户接收到重要信息,并在必要时确保操作正确。

我们来设计简单的模态弹窗,模态弹窗需要有两个页面作为交互基点,我们先创建一个新的SwiftUI页面,命名为NewNoteView。如下图所示:

创建完成后,回到ContentView,定义模态弹窗的触发条件,通常使用Bool值作为触发条件,如下代码所示:

@State var showNewNoteView = false

模态弹窗的跳转需要使用到使用.sheet修饰符,绑定声明好的Bool值变量,如下代码所示:

NavigationView {
	ZStack {
		if noteItems.count == 0 {
			noDataView()
		} else {
			VStack {
				searchBarView()
				NoteListView()
			}
		}
		newBtnView()
	}
	.navigationBarTitle("念头笔记", displayMode: .inline)
}.sheet(isPresented: $showNewNoteView) {
            NewNoteView()
}

上述代码中,我们将.sheet修饰符修饰整个NavigationView包裹的首页视图,isPresented模态弹窗的触发动作绑定声明好的变量showNewNoteView,跳转的页面为新创建的NewNoteView

最后是跳转的动作,当用户点击“新建笔记”按钮时,切换showNewNoteView以触发打开模态弹窗的动作,如下代码所示:

self.showNewNoteView = true

运行预览效果如下图所示:

新建笔记页面

来到NewNoteView页面,我们来完成“新建笔记”页面的相关样式内容,依旧是从上至下拆解页面元素,如下图所示:

“新建笔记”页面有顶部导航栏、标题输入框、内容输入框三块要素组成。

顶部导航栏

首先是顶部导航栏,顶栏由“关闭”按钮,“新建笔记”标题,“完成”按钮组成,标题我们可以使用NavigationView构建,如下代码所示:

NavigationView {
	Text("Hello, World!")
		.navigationBarTitle("新建笔记",displayMode: .inline)
}

按钮部分,我们可以单独构建按钮的样式,如下代码所示:

// MARK: 关闭按钮

func closeBtnView() -> some View {
    Button(action: {
    }) {
        Image(systemName: "xmark.circle.fill")
            .font(.system(size: 17))
            .foregroundColor(.gray)
    }
}

// MARK: 完成按钮

func saveBtnView() -> some View {
    Button(action: {
    }) {
        Text("完成")
            .font(.system(size: 17))
    }
}

下一步,我们只需要将这两个按钮加到NavigationView顶部导航上就可以了,这里使用到的修饰符是navigationBarItems,如下代码所示:

NavigationView {
    Text("Hello, World!")
        .navigationBarTitle("新建笔记", displayMode: .inline)
        .navigationBarItems(leading:closeBtnView(),trailing: saveBtnView())
}

标题输入框

其次是标题输入框,输入框使用到TextField输入框组件,我们需要提前使用@State声明好参数,如下代码所示:

@State var title: String
@State var isEditing = false

上述代码中,title变量为标题输入框输入的内容,isEditing变量则是检测当前输入框是否正在输入,用于状态检测和交互使用。

标题输入框的内容样式构建如下:

// MARK: 标题输入框

func titleView() -> some View {
    TextField("请输入标题", text: $title, onEditingChanged: { editingChanged in
        self.isEditing = editingChanged
    })
    .padding()
}

内容输入框

再次是内容输入框,与标题输入框不同,内容输入框可以输入多行的文字,因此这里使用的是TextEditor多行文本输入框,和TextField输入框组件使用方法类似,首先需要使用@State声明好参数,如下代码所示:

 @State var content: String

上述代码中,content变量为TextEditor内容输入框输入的内容,紧接着,内容输入框的内容样式构建如下:

// MARK: 内容输入框

func contentView() -> some View {
    ZStack(alignment: .topLeading) {
        TextEditor(text: $content)
            .font(.system(size: 17))
            .padding()
        if content.isEmpty {
            Text("请输入内容")
                .foregroundColor(Color(UIColor.placeholderText))
                .padding(20)
        }
    }
}

上述代码中,我们创建了一个新的视图contentView内容输入框视图,在这里由于TextEditor多行文本输入框不像TextField输入框组件可以设置提示文字,因此输入开发者自己实现。

我们实现的方法也很简单,使用ZStack层叠容器,当content输入的内容为空的时候,构建展示一个“请输入内容”文字,如此便实现了多行文本框的提示文字效果。

标题输入框titleView和内容输入框contentView的布局方式为纵向布局关系,我们可以使用VStack垂直布局使用,分割线部分可以使用SwiftUI提供的Divider分割线容器。如下代码所示:

NavigationView {
    VStack{
        Divider()
        titleView()
        Divider()
        contentView()
    }
        .navigationBarTitle("新建笔记", displayMode: .inline)
        .navigationBarItems(leading: closeBtnView(), trailing: saveBtnView())
}

运行预览效果如下图所示:

交互逻辑

完成了“新建笔记”的页面样式后,我们再来完成基础的页面交互。

关闭弹窗

在ContentView首页我们使用.sheet修饰符触发打开“新建笔记“页面,并使用showNewNoteView触发打开模态弹窗,当showNewNoteView切换为true时,打开“新建笔记”弹窗页面。

这里可以使用@Binding在NewNoteView页面双向绑定showNewNoteView参数,然后当点击“关闭”按钮时,切换showNewNoteView状态。

首先声明showNewNoteView参数,如下代码所示:

@Binding var showNewNoteView: Bool

我们将点击“关闭”按钮时,切换showNewNoteView状态,如下代码所示:

self.showNewNoteView = false

由于NewNoteView新建笔记页面也在使用PreviewProvider预览,因此在该页面声明的所有没有默认值的变量,都需要创建变量默认值才能进行预览。

完成后,我们还需要回到ContentView首页,对NewNoteView的参数进行双向绑定,如下图所示:

项目预览

运行预览效果如下图所示:

QQ20220919-231956-HD.gif

本章小结

由于项目较长,这里将分成几个章节完成,请按耐住性子一步一步完成。

在本章中,我们完成了“新建笔记”页面,首次使用了TextField输入框、TextEditor多行文本输入框,并学会使用@Binding声明可双向绑定的变量,并实现“首页”页面、“新建笔记”页面之间的逻辑交互。

本章内容不多,也不太难,重要的是思维方式。很多时候页面的构建方式都是自上而下,然后逐步拆解的过程,而从代码来看又呈现自下而上构建页面的逻辑。

在编程过程中,每一块内容我们几乎都采用抽离的方式,除了使用func定义View视图的方式,也可以更常用地使用建立子视图的方式创建View视图,因人而异吧。

接下来的章节,我们将继续完成交互逻辑部分,请保持期待吧~

快来动手试试吧~

版权声明

本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!