在本章中,你将学会运用SwiftUI
建立一个计时器App
。
前言
为了愈加了解和了解SwiftUI
,本系列将从实战视点出发完成100个SwiftUI项目,方便大家更好地学习和掌握SwiftUI
。
这一起也是对自己学习SwiftUI
进程的知识整理。
如有错误,以你为准。
项目建立
首要,创立一个新的SwiftUI
项目,命名为Timer
。
逻辑分析
计时器的原理比较简略,关于用户而言首要操作就3个:开端、暂停、复位。
用户点击开端按钮,计时器上的文字开端按照时刻累加,点击暂停时,计时器的数字中止并展现暂停时的数字,点击复位按钮,则计时器从头归零。
但其间仍是会有一些简略遗忘的逻辑,比方刚开端时,用户只能点击开端按钮,体系躲藏或许禁用暂停和复位操作。
而计时器开端计时后,用户只能点击暂停操作,体系躲藏或许禁用开端和复位操作。点击暂停按钮后,用户才能点击复位操作。
页面款式
了解完计时器的逻辑之后,咱们来完成页面款式的规划。
App标题
App
标题,咱们运用Text
文本作为标题款式,示例:
// 计时器标题
func titleView() -> some View {
HStack {
Text("计时器")
.font(.title)
.fontWeight(.bold)
Spacer()
}
}
为了让App
愈加美观,咱们在Assets
文件中导入了一张图片作为App
主视图的展现,示例:
// 图片
func dinnerImageView() -> some View {
Image("dinner")
.resizable()
.scaledToFit()
}
上述代码中,咱们给Image
图片设置了2个修饰符,进行等比例缩放。
这样,咱们就得到了标题和App
示例图片。
计时文字
计时文字部分,首要咱们需求声明一个变量存储咱们的计时数值,示例:
@State var timeText: String = "0.00"
然后,咱们可以运用Text
绑定并展现计时的文字,示例:
// 计时文字
func timerTextView() -> some View {
Text(timeText)
.font(.system(size: 48))
.padding(.horizontal)
.background(Color(.systemGray6))
.cornerRadius(8)
}
上述代码中,咱们运用Text
文字款式,绑定timeText
参数,并运用了一些修饰符设置了文字的巨细、计时文字的排布方位、背景色彩和圆角。
操作按钮
关于操作按钮部分,咱们需求3个按钮:开端按钮、暂停按钮、复位按钮。
开端按钮
开端按钮部分,由于和其他按钮款式别离,咱们可以单独构建,示例:
// 开端按钮
func startBtn() -> some View {
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.green)
Image(systemName: "play.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}
}
上述代码中,咱们构建了一个圆形背景,设置巨细为60*60,色彩为绿色。按钮本身运用Apple
提供的体系图标,设置尺度为32,填充色彩为白色。
暂停和复位
当咱们点击开端按钮,那么操作按钮就会变成2个:暂停和复位。
其间,暂停按钮有2种状况,一种是未操作时,一种则是现已点击暂停,因此咱们需求声明一个是否暂停的变量来存储它,示例:
@State var isPause: Bool = false
然后和开端按钮相同,咱们构建暂停和复位按钮的款式,示例:
// 暂停和复位按钮
func pauseAndResetBtn() -> some View {
HStack(spacing: 60) {
// 暂停按钮
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.red)
Image(systemName: isPause ? "play.fill" : "pause.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}
// 复位按钮
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.blue)
Image(systemName: "arrow.uturn.backward.circle.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}
}
}
全体款式布局
全体款式部分,由于操作区存在2种款式,一种是点击开端前,一种是点击计时开端,咱们还需求声明一种是否开端的状况存储它,示例:
@State var isStart: Bool = true
最后是款式的全体部分,咱们在body
中布局款式,示例:
var body: some View {
VStack(spacing: 20) {
titleView()
dinnerImageView()
timerTextView()
Spacer()
//操作按钮
if isStart {
pauseAndResetBtn()
} else {
startBtn()
}
}
.padding()
.padding(.bottom, 40)
}
这样,款式部分咱们就规划好了。
计时办法
办法创立
计时的办法首要运用到了Timer
函数,首要咱们要声明两个变量,一个用来更新复位后的时刻,一个用来计数,示例:
@State private var startTime = Date()
@State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
然后创立两个办法,一个用来开端计数,一个用来中止计数,示例:
// 开端计时办法
func startTimer() {
timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
}
// 中止计时办法
func stopTimer() {
timer.upstream.connect().cancel()
}
开端计时
然后在点击开端按钮时,调用开端计数的办法,示例:
// 开端按钮
func startBtn() -> some View {
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.green)
Image(systemName: "play.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}.onTapGesture {
self.isStart = true
timeText = "0.00"
startTime = Date()
self.startTimer()
}
}
上述代码中,咱们运用onTapGesture
修饰符给开端按钮添加交互,当咱们点击开端按钮时,首要转换isStart
状况,这样咱们的操作按钮款式就会切换到暂停和复位的操作。
然后是timeText
初始化展现内容为0.00
,然后startTime
从当时timeText
开端,再调用startTimer
办法开端计时。
中止计时
中止计时办法也很简略,不过这里要注意的是,暂停按钮承载了暂时和继续计时的操作,示例:
// 暂停和复位按钮
func pauseAndResetBtn() -> some View {
HStack(spacing: 60) {
// 暂停按钮
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.red)
Image(systemName: isPause ? "play.fill" : "pause.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}
.onTapGesture {
if !isPause {
self.isPause = true
self.stopTimer()
} else {
self.isPause = false
self.startTimer()
}
}
}
上述代码中,咱们也给暂停按钮添加了交互,当咱们isPause
没有中止时,咱们点击暂停按钮,则isPause
状况切换为中止,这样咱们对应的暂停按钮的款式也会切换,然后调用stopTimer
中止计时的办法。
而当咱们暂停的时候点击暂停按钮时,咱们切换isPause
状况更新款式,一起又调用startTimer
开端计时的办法继续计时。
计时复位
关于复位操作,咱们要简略许多,咱们只需求在点击时将isStart
、isPause
更新为false
,最后把计时展现文字timeText
更新为0.00
就可以了。代码如下:
// 复位按钮
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.blue)
Image(systemName: "arrow.uturn.backward.circle.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}
.onTapGesture {
self.isStart = false
self.isPause = false
timeText = "0.00"
}
完成后,咱们预览下项目效果。
项目预览
本章完好代码
import SwiftUI
struct ContentView: View {
@State var timeText: String = "0.00"
@State var isPause: Bool = false
@State var isStart: Bool = false
@State private var startTime = Date()
@State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
VStack(spacing: 20) {
titleView()
dinnerImageView()
timerTextView()
Spacer()
// 操作按钮
if isStart {
pauseAndResetBtn()
} else {
startBtn()
}
}
.padding()
.padding(.bottom, 40)
}
// 计时器标题
func titleView() -> some View {
HStack {
Text("计时器")
.font(.title)
.fontWeight(.bold)
Spacer()
}
}
// 图片
func dinnerImageView() -> some View {
Image("dinner")
.resizable()
.scaledToFit()
}
// 计时文字
func timerTextView() -> some View {
Text(timeText)
.font(.system(size: 48))
.padding(.horizontal)
.background(Color(.systemGray6))
.cornerRadius(8)
.onReceive(timer) { _ in
if self.isStart {
timeText = String(format: "%.2f", Date().timeIntervalSince(self.startTime))
}
}
}
// 开端按钮
func startBtn() -> some View {
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.green)
Image(systemName: "play.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}.onTapGesture {
self.isStart = true
timeText = "0.00"
startTime = Date()
self.startTimer()
}
}
// 暂停和复位按钮
func pauseAndResetBtn() -> some View {
HStack(spacing: 60) {
// 暂停按钮
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.red)
Image(systemName: isPause ? "play.fill" : "pause.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}
.onTapGesture {
if !isPause {
self.isPause = true
self.stopTimer()
} else {
self.isPause = false
self.startTimer()
}
}
// 复位按钮
ZStack {
Circle()
.frame(width: 60, height: 60)
.foregroundColor(.blue)
Image(systemName: "arrow.uturn.backward.circle.fill")
.foregroundColor(.white)
.font(.system(size: 32))
}
.onTapGesture {
self.isStart = false
self.isPause = false
timeText = "0.00"
}
}
}
// 开端计时办法
func startTimer() {
timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
}
// 中止计时办法
func stopTimer() {
timer.upstream.connect().cancel()
}
}
不错不错!
如果本专栏对你有帮助,无妨点赞、谈论、重视~
我正在参加技术社区创作者签约计划招募活动,点击链接报名投稿。