在本章中,你将学会运用SwiftUI建立一个计时器App

前言

为了愈加了解和了解SwiftUI,本系列将从实战视点出发完成100个SwiftUI项目,方便大家更好地学习和掌握SwiftUI

这一起也是对自己学习SwiftUI进程的知识整理。

如有错误,以你为准。

项目建立

首要,创立一个新的SwiftUI项目,命名为Timer

SwiftUI100天:使用SwiftUI搭建一个计时器App

逻辑分析

计时器的原理比较简略,关于用户而言首要操作就3个:开端暂停复位

用户点击开端按钮,计时器上的文字开端按照时刻累加点击暂停时,计时器的数字中止并展现暂停时的数字,点击复位按钮,则计时器从头归零

但其间仍是会有一些简略遗忘的逻辑,比方刚开端时,用户只能点击开端按钮,体系躲藏或许禁用暂停和复位操作。

而计时器开端计时后,用户只能点击暂停操作,体系躲藏或许禁用开端和复位操作。点击暂停按钮后,用户才能点击复位操作。

页面款式

了解完计时器的逻辑之后,咱们来完成页面款式的规划。

SwiftUI100天:使用SwiftUI搭建一个计时器App

App标题

App标题,咱们运用Text文本作为标题款式,示例:

// 计时器标题
func titleView() -> some View {
HStack {
Text("计时器")
.font(.title)
.fontWeight(.bold)
Spacer()
}
}

SwiftUI100天:使用SwiftUI搭建一个计时器App

为了让App愈加美观,咱们在Assets文件中导入了一张图片作为App主视图的展现,示例:

// 图片
func dinnerImageView() -> some View {
Image("dinner")
.resizable()
.scaledToFit()
}

SwiftUI100天:使用SwiftUI搭建一个计时器App

上述代码中,咱们给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)
}

SwiftUI100天:使用SwiftUI搭建一个计时器App

上述代码中,咱们运用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))
}
}

SwiftUI100天:使用SwiftUI搭建一个计时器App

上述代码中,咱们构建了一个圆形背景,设置巨细为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))
}
}
}

SwiftUI100天:使用SwiftUI搭建一个计时器App

全体款式布局

全体款式部分,由于操作区存在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)
}

SwiftUI100天:使用SwiftUI搭建一个计时器App

这样,款式部分咱们就规划好了。

计时办法

办法创立

计时的办法首要运用到了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开端计时的办法继续计时。

计时复位

关于复位操作,咱们要简略许多,咱们只需求在点击时将isStartisPause更新为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"
}

完成后,咱们预览下项目效果。

项目预览

SwiftUI100天:使用SwiftUI搭建一个计时器App

本章完好代码

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()
}
}

不错不错!

如果本专栏对你有帮助,无妨点赞、谈论、重视~

我正在参加技术社区创作者签约计划招募活动,点击链接报名投稿。