项目来源
前段时间稀土客户端上线了“闪念笔记
”功用,作为尝鲜一族便稍微体验了一下。
闪念笔记界面简约操作容易上手,相比较印象笔记、有道云笔记等归纳型笔记运用
,当时闪念笔记当时专注于笔记本身,只作为一种常识储藏
的记载东西。
深入研究后发现了点意外之喜,以稀土流量口结合的闪念笔记,与Web端运用
、浏览器剪藏插件
相融合能够构成闭环产品生态体系。
不难猜测,在笔记东西功用完善和运用率掩盖必定市场份额后,必将正式打造移动端和桌面版运用,切入笔记类东西范畴杀出一条血路。
当然,以上仅仅只是猜测。
进入正题,本章咱们来建立第一个SwiftUI
实战项目,从0到1完结一款iOS笔记App。
需求分析
一款最小MVP的笔记App是怎样的呢?及时记载自己的想法、想法,查找自己曾经记载的笔记,这是一款笔记最基本的功用需求点。
咱们先整理产品架构图
,如下图所示:
一款最简单的笔记软件,包含2个中心页面:
- 主页界面:展现笔记列表、新建笔记按钮,若是做功用强化,能够加上查找栏;
- 新建按钮:点击按钮,翻开“新建页面”弹窗,或许进入新的页面,修改完结关闭/回到主页;
产品规划
下一步,咱们来完结产品的原型规划
,构建2个页面,并将产品架构中的元素信息
转换为页面功用
,如下图所示:
用户首次进入时,念想笔记将会展现一个空白页,空白页面由标题、缺省图、指引文字、新建笔记按钮组成,首要引导用户进行首要功用的运用。
当用户点击新建笔记按钮时,引发“新建笔记
”弹窗页面,弹窗页面相比较进入一个新的页面,用户思想不会被强行打断,这是一种很好的交互。
在“新建笔记”页面中,首要操作为“回来
”按钮、“完结
”按钮、标题
输入框、内容
输入框。
用户点击“回来”按钮,则向下关闭该弹窗,并清空该页面已输入内容;
当用户点击“完结”按钮时,需求判别
“标题输入框”、“内容输入框”是否有键入的文字,且契合预设的规矩(文字数量等);
当满足规矩时,则吐司提示保存成功
,并关闭该弹窗页面,且在笔记列表中插入一条新的笔记
。若不满足规矩(校验标题和内容是否为空),则吐司冒泡提示相关信息(标题不能为空、内容不能为空)。
为更好地指引证户填写,标题输入框需求有提示文字,当文字键入时,清空提示文字,内容输入框同理。
对于查找功用,中心点在于依据内容关键字进行查找,并实时进行反应。当然一般因为性能问题,会考虑输入后点击“查找”触发查找功用的交互方法。
以上便是简单的需求文档的撰写。
UI规划
下面咱们运用AdobeXD
依据产品原型图绘制UI规划稿
,UI规划稿需求包含一切的页面及其交互动作,如下图所示:
UI规划需求依据产品原型的元素和内容,结合当时市场上的常用交互逻辑和规划标准,输出高保真
的规划稿。其中包含各项元素组件的尺度标准、文字标准、交互标准等,旨在为前端开发人员供给很好的款式开发指引
。
UI规划师和产品司理的分工常常是产品司理供给产品原型Demo
,UI规划师
确定产品的主体风格和规划标准,输出UI规划稿
,再与产品司理或许与业务团队进行评审,经过后方可进行切图,移交给下一流程。
UI规划稿最为App终究出现的作用产品,前端工程师需求依据UI规划稿到达一比一复原
,后期也能够与UI规划稿进行比照进行App款式验收。
在这儿科普一个概念,切图。
切图是UI规划师将已经完结好的静态页面中的元素运用东西进行切分,与静态页面别离,示例:图标按钮、插图。UI规划师将这些需求在实践开发进程中需求导入运用的资料从静态页面上“切”下来,便能够鄙人面的协同东西中导出不同尺度的图片进行运用。
UI规划师切好图后,需求再凭借一个东西交付给前端规划师,这儿推荐的东西是PxCook像素大厨
。
装置完结,AdobeXD将会装置对应的插件,挑选文件 > 导出 > PxCook
。如下图所示:
PxCook将会被引发,并创立一个新项目,输入“想法笔记
”,挑选类型为iOS
,点击“创立本地项目
”,如下图所示:
挑选导入画板,PxCook会主动勾选由AdobeXD导出的一切面板,咱们坚持全选状况,点击“导入
”,如下图所示:
如此,咱们便将AdobeXD中的UI规划稿导入到PxCook中了。
在PxCook中,咱们能够看到UI规划稿规划的元素的尺度、元素之间的相对距离,方便于开发者依据UI规划稿进行前端静态页面的开发作业,如下图所示:
实战编程
接下来,咱们正式进入到编程阶段,翻开Xcode
开发东西,点击Create a new Xcode project
,将新项目命名为IdeaNote
,如下弹窗所示:
主页-缺省图
点击视图东西栏的Assets.xcassets
文件,拖入主页缺省图
的图片,如下图所示:
回到ContentView文件,在用户初始进入时,映入眼帘的是一个初始的款式,它由图片
和文字
组成,如下代码所示:
// 缺省图
func noDataView() -> some View {
VStack(alignment: .center,spacing: 20) {
Image("mainImage")
.resizable()
.scaledToFit()
.frame(width: 240)
Text("记载下这个世界的点滴")
.font(.system(size: 17))
.bold()
.foregroundColor(.gray)
}
}
上述代码中,咱们创立了一个新的缺省视图noDataView
。
图片与文字运用VStackc笔直布局容器,容器内的元素alignment对齐方法为center居中对齐,元素的spacing距离为20。
图片引证之前导入的mainImage
图片,为了坚持图片在视图内的展现作用,运用resizable修饰符调整图片大小,运用scaledToFit修饰符坚持其宽高比,防止图片变形,最终运用frame修饰符设置图片的大小为240。
文字部分内容设置为“记载下这个世界的点滴
”,运用font修饰符设置文字大小为17,运用bold修饰符使得文字加粗,运用foregroundColor修饰符设置文字填充色彩。
运转预览作用如下图所示:
新建笔记按钮款式也能够按照上述创立视图的方法,如下代码所示:
// 新建笔记按钮
func newBtnView() -> some View {
VStack {
Spacer()
HStack {
Spacer()
Button(action: {
}) {
Image(systemName: "plus.circle.fill")
.font(.system(size: 48))
.foregroundColor(.blue)
}
}
}
.padding(.bottom, 32)
.padding(.trailing, 32)
}
上述代码中,咱们创立了一个新的新建笔记按钮视图newBtnView
。
新建笔记按钮的中心是一个图标按钮,运用Image组件引证体系供给的图标“plus.circle.fill
”,按钮的交互能够直接运用Button
组件。
为了使按钮文字放置在屏幕右下角,咱们运用VStack笔直容器和Spacer占位视图将按钮撑在底部,然后再运用HStack横向容器和Spacer占位视图再把按钮撑到右边,咱们再运用padding在bottom底部和trailing右边都留出距离。
如此咱们就完结新建笔记按钮的款式,最终咱们在主视图中与noDataView缺省图运用ZStack层叠容器包裹,运转预览作用如下图所示:
还差点什么?标题。标题部分能够直接运用NavigationView
导航视图与navigationTitle修饰符设置标题,如下代码所示:
NavigationView {
ZStack {
noDataView()
newBtnView()
}.navigationBarTitle("想法笔记", displayMode: .inline)
}
主页-列表页
当用户新建了笔记之后,主页便从本来的缺省视图转换为列表视图,列表视图中的首要两块视图为“查找栏
”、“列表栏
”,如下图所示:
咱们至上而下构建页面,首先是查找栏。从规划稿中,咱们能够知道查找栏由一个查找图标、查找输入框、铲除按钮组成。因为会运用输入框TextField
,因而需求提早声明绑定的变量,如下代码所示:
@State var searchText = ""
// MARK: 查找
func searchBarView() -> some View {
TextField("查找内容", text: $searchText)
.padding(7)
.padding(.horizontal, 25)
.background(Color(.systemGray6))
.cornerRadius(8)
.overlay(
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 8)
// 修改时显现铲除按钮
if searchText != "" {
Button(action: {
self.searchText = ""
}) {
Image(systemName: "multiply.circle.fill")
.foregroundColor(.gray)
.padding(.trailing, 8)
}
}
}
)
.padding(.horizontal, 10)
}
上述代码中,咱们创立了一个新的查找栏视图searchBarView
。
查找栏运用TextField输入框组件,内容text
部分绑定声明好的变量searchText
。
款式部分运用padding修饰符撑开一段距离,运用background修饰符设置布景填充色彩为灰色,运用cornerRadius修饰符设置视图的圆角度数。
输入框右边有一个“查找
”的图标,这儿运用overlay
修饰符在输入框层叠一个“查找”图标和一个“铲除”图标按钮。
其中“铲除”图标的交互逻辑是,判别输入框内输入的文字searchText
是否为空,假如不为空,则展现“铲除”按钮,当点击“铲除”按钮的时分清空searchText
内容。
运转预览作用如下图所示:
接下来是列表部分,仍旧拆解下列表的元素,列表由记载时间、笔记标题、笔记内容、更多按钮组成,如下图所示:
拆解好元素后,因为列表不是固定写好的内容,而是由用户修改输入的内容,因而咱们需求构建数据模型。
在Xcode视图窗口右键,挑选New File
,创立一个新的Swift
文件,名称为Model.swift
。如下图所示:
创立完结后,录入以下代码:
import SwiftUI
class NoteItem: ObservableObject, Identifiable {
var id = UUID()
@Published var writeTime: String = ""
@Published var title: String = ""
@Published var content: String = ""
// 实例化
init(writeTime: String, title: String, content: String) {
self.writeTime = writeTime
self.title = title
self.content = content
}
}
上述代码中,咱们创立了一个类NoteItem
,遵循ObservableObject
可被观察目标协议和Identifiable
可被辨认协议。
在NoteItem类里边有三个参数:writeTime
录入时间、title
标题、content
内容。并且在ObservableObject协议需求运用@Published
定义,这样才能在参数改变的时分检测到变化。因为运用Identifiable可被辨认协议,因而需求声明一个id
为UUID()
。
定义好Model数据模型后,回到ContentView
文件,咱们来创立列表视图,如下代码所示:
// MARK: 列表内容
struct NoteListRow: View {
@ObservedObject var noteItem: NoteItem
var body: some View {
HStack {
VStack(alignment: .leading, spacing: 10) {
Text(noteItem.writeTime)
.font(.system(size: 14))
.foregroundColor(.gray)
Text(noteItem.title)
.font(.system(size: 17))
.foregroundColor(.black)
Text(noteItem.content)
.font(.system(size: 14))
.foregroundColor(.gray)
.lineLimit(1)
.multilineTextAlignment(.leading)
}
Spacer()
Button(action: {
}) {
Image(systemName: "ellipsis")
.foregroundColor(.gray)
.font(.system(size: 23))
}
}
}
}
上述代码中,咱们先创立了一个单条列表视图NoteListRow
,运用@ObservedObject
引证监听实例目标的类NoteItem
。
在构建列表视图的款式上,和根底构建视图的方法一致,只是本来固定录入的参数,变成了来自于NoteItem类的参数,示例:标题
,运用noteItem.title
。
三个文本运用VStack纵向布局容器,设置左对齐以及距离为10。最终运用HStack横向布局容器包裹三个文本和“更多”按钮。因为noteItem.content内容文字或许很长,咱们只需求一行,因而能够运用lineLimit
限制长度为1行省掉。
如此,便构建完结了单条笔记的款式。
然后咱们根据单条笔记的款式构建列表视图,如下代码所示:
// MARK: 列表
struct NoteListView: View {
@State var noteItems: [NoteItem] = [NoteItem(writeTime: "2022.09.17", title: "第一条笔记", content: "快来运用想法笔记记载日子吧~快来运用想法笔记记载日子吧~")]
var body: some View {
List {
ForEach(noteItems) { noteItem in
NoteListRow(noteItem: noteItem)
}
}
.listStyle(InsetListStyle())
}
}
上述代码中,咱们创立了一个NoteListView
列表视图,运用@State
声明一个数组noteItems
,并赋予noteItems来源于NoteItem数组类并赋予内容。
在主体body
部分,运用List
列表组件和ForEach
循环遍历noteItems
数组的数据,并传递参数给NoteListRow
。
List列表款式部分,因为SwiftUI默许款式是圆角矩形分组的方法,这边还需求设置List列表款式为InsetListStyle
。
咱们在ContentView的body中运用查找栏视图和列表视图,如下代码所示:
NavigationView {
ZStack {
VStack {
searchBarView()
NoteListView()
}
newBtnView()
}.navigationBarTitle("想法笔记", displayMode: .inline)
}
运转预览作用如下图所示:
主页-页面判别
上述编程进程中,咱们完结了缺省页和列表页,它们之间的交互逻辑是:当笔记列表中没有笔记时,App将展现缺省页,当存在笔记时,展现列表页。
能够先引进NoteItem
数组类,如下代码所示:
@State var noteItems: [NoteItem] = [NoteItem(writeTime: "2022.09.17", title: "第一条笔记", content: "快来运用想法笔记记载日子吧~快来运用想法笔记记载日子吧~")]
然后咱们就能够依据noteItems
数组的数量作为判别条件,如下代码所示:
NavigationView {
ZStack {
if noteItems.count == 0 {
noDataView()
} else {
VStack {
searchBarView()
NoteListView()
}
}
newBtnView()
}.navigationBarTitle("想法笔记", displayMode: .inline)
}
上述代码中,当noteItems
数组中的数量为0
时,则展现noDataView
缺省页,不然则展现查找栏+笔记列表
组成的列表页。
运转预览作用如下图所示:
本章小结
因为项目较长,这儿将分红几个章节完结,请按耐住性质一步一步完结。
在本章中,咱们从产品规划开端,经过需求分析、产品规划、UI规划、实战编程等阶段来从0到1完结一款iOS笔记App,其中涉及到各个阶段不同职业的作业细节,希望能给大家对一款App全生命周期进程有一个大约的认识。
快来动手试试吧~
版权声明
本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!