轻学编程|Swift 教程 009:计数器
轻学编程|Swift 教程 009:计数器

轻学编程|Swift 教程 009:计数器

在前两节课中,我们学习了变量、常量、按钮和方法。

这一节课,我们将把这些知识结合起来,制作一个简单的计数器应用。

在这个例子中,我们将学习如何保存和改变变量,以及使用 @State 管理变量。

实现计数器

计数器通常用于记录需要不断累加的数量。例如,跳绳次数、跑步圈数等计数场景。

下面我们来实现一个简单的计数器:显示一个数字和按钮,点击按钮让数字增加。

显示数字

首先,我们使用 Text 显示数字。

Text("0")

但是,如果数字需要变化,就不能直接写死”0″,而应该使用变量保存数字。

var num = 0

然后在 Text 中显示变量:

Text("\(num)")

这里使用了字符串插值 \\(),将数字转换成字符串进行显示。

按钮增加数字

接下来,我们添加一个按钮。

Button("+") {

}
.buttonStyle(.borderedProminent)

按钮被点击时,我们希望数字增加。

因此可以在按钮中修改变量:

Button("+") {
    num = num + 1
}
.buttonStyle(.borderedProminent)

这段代码表示:当点击按钮时,会计算 num + 1,并将结果赋值给 num。

更简洁的写法

Swift 提供了一种更简洁的写法:

num += 1

它等价于:

num = num + 1

这种写法叫做“复合赋值运算符”。

Swift 中常见的复合赋值运算符有:

+=   加法赋值
-=   减法赋值
*=   乘法赋值
/=   除法赋值
%=   取余赋值

例如:

num -= 2   // num = num - 2
num *= 3   // num = num * 3

完整代码

现在,我们可以写出一个简单的计数器:

struct ContentView: View {
    var num = 0
    var body: some View {
        VStack {
	    Text("\(num)")
            Button("+") {
                num += 1
             }
             .buttonStyle(.borderedProminent)
	}
    }
}

但是,此时代码无法正常运行。

出现错误

Xcode 会提示:

Cannot assign to property: 'self' is immutable

表示无法修改属性,因为视图是不可修改的。

为什么会这样?

SwiftUI 的工作原理

在 SwiftUI 中:界面是由数据驱动的,这意味着当数据变化时,界面会自动刷新。

但需要注意的是,只有在状态(如 @State、 @Binding)发生变化时,SwiftUI 才会监控变化并刷新视图。

如果使用普通变量,SwiftUI 只会在视图创建时读取一次变量的值,之后即使变量发生改变,界面也不会更新。

例如:

var num = 0   // 0
num = 1   // 0

在上面的例子中,num 是普通变量,虽然它的值发生了变化,但 SwiftUI 不会更新视图,界面依然会显示初始的 0。

因此,只有当数据被标记为状态(如使用 @State、@Binding 等属性包装器)时,SwiftUI 才会自动监控它的变化并更新视图。

@State 属性包装器

在 SwiftUI 中,如果需要修改变量并更新视图,就必须使用 @State。

@State var num = 0

@State 会让 SwiftUI 监听这个变量。

当变量发生变化时,SwiftUI 会重新计算视图,并更新视图。

完整代码:

struct ContentView: View {
    @State var num = 0
    var body: some View {
        VStack {
	    Text("\(num)")
            Button("+") {
              num += 1
            }
            .buttonStyle(.borderedProminent)
	}
    }
}

现在,当点击按钮时:

num += 1

SwiftUI 会检测到 num 发生变化,然后自动更新视图。

@State 的使用规则

在实际开发中,@State 通常遵循两个规则。

1. @State 通常使用 private

@State 变量通常只在当前类型(如当前 View 结构体)中使用,因此一般会写成:

@State private var num = 0

这样可以避免其他视图直接访问或修改这个变量。

现在还没有学习 private,可以简单的理解为:

private 修饰的内容只能在当前视图中使用,外面的视图无法使用。

2. @State 只用于当前视图的状态

@State 适用于当前 View 自己管理的数据。

例如,输入框内容、开关状态。

@State private var isOn = false
@State private var text = ""

如果数据需要在多个视图之间共享,就需要使用其他状态类型,例如 @Binding 或 @Observable。

这些内容会在后面的课程中学习。

减少按钮

计数器除了增加,还可以减少。

我们可以添加一个 – 按钮。

Button("-") {
    num -= 1
}
.buttonStyle(.borderedProminent)

然后使用 HStack 水平排列按钮:

HStack {
    Button("+") {
        num += 1
    }
    .buttonStyle(.borderedProminent)
    Button("-") {
        num -= 1
    }
    .buttonStyle(.borderedProminent)
}

效果:

点击 + 按钮时,数字会加 1;点击 – 按钮时,数字会减 1。

重置按钮

我们还可以添加一个重置按钮,将数字变为 0。

Button("0") {
    num = 0
}
.buttonStyle(.borderedProminent)

当点击重置按钮时,num 被赋值为 0。

效果:

点击 0 按钮时,数字变为 0。

自定义按钮

目前我们的按钮还只能显示文本:

Button("+") {

}

这种写法的按钮只能显示文本内容,例如 + 或 -。

在实际开发中,我们通常希望按钮的外观更加丰富,例如使用图标或其他视图。

SwiftUI 允许我们自定义按钮的显示内容,这时可以使用按钮的另一种写法:

Button(action: {

}, label: {

})

在这个写法中,action 表示按钮点击执行的代码,label 显示按钮的视图。

例如,我们可以让按钮显示一个 SF Symbols 图标:

Button(action: {
    num += 1
}, label: {
    Image(systemName: "plus.app")
})

这样按钮显示的就不再是文本,而是一个图标。

优化计数器界面

为了让计数器的界面更清晰,我们还可以调整数字的显示样式。例如放大数字:

Text("\(num)")
    .font(.largeTitle)
    .fontWeight(.bold)

接下来,我们把按钮的显示内容也改为图标,并适当放大图标尺寸:

Button(action: {
    num += 1
}, label: {
    Image(systemName: "plus.app")
        .imageScale(.large)
})

同样地,我们也可以把减少按钮和重置按钮改为图标形式:

Button(action: {
    num -= 1
}, label: {
    Image(systemName: "minus.square")
        .imageScale(.large)
})

Button(action: {
    num = 0
}, label: {
    Image(systemName: "arrow.trianglehead.2.clockwise.rotate.90")
        .imageScale(.large)
})

现在按钮显示的是 SF Symbols 图标,界面也会更加直观。

总结

本节课通过实现一个简单的计数器,综合练习了变量、Text 和 Button 的使用。我们使用变量保存数字,并通过按钮控制数字增加或减少,同时学习了复合赋值运算符,例如 num += 1 和 num -= 1。

在实现过程中,我们了解了 SwiftUI 的工作方式:界面由数据驱动。当数据发生变化时,界面才会刷新。因此需要使用 @State 保存可变化的状态,当 @State 变量改变时,SwiftUI 会自动更新视图。

最后,我们对界面进行了简单优化,调整了 Text 的字体样式,并使用 Button(action:label:) 自定义按钮内容,使按钮可以显示图标,从而让计数器界面更加清晰和美观。

现在,我们已经接触到了 SwiftUI 视图的基本使用方式。在 SwiftUI 中,大部分界面都是通过变量驱动的。当变量发生变化时,SwiftUI 会自动更新视图。因此,在开发 SwiftUI 应用时,我们通常需要先设计好需要保存的数据,再通过这些数据来构建界面和交互逻辑。

完整代码

struct ContentView: View {
    @State var num = 0
    var body: some View {
        VStack(spacing: 20) {
            Text("\(num)")
                .font(.largeTitle)
                .fontWeight(.bold)
            HStack {
                Button(action: {
                    num += 1
                }, label: {
                    Image(systemName: "plus.app")
                        .imageScale(.large)
                })
                
                Button(action: {
                    num -= 1
                }, label: {
                    Image(systemName: "minus.square")
                        .imageScale(.large)
                })
                
                Button(action: {
                    num = 0
                }, label: {
                    Image(systemName: "arrow.trianglehead.2.clockwise.rotate.90")
                        .imageScale(.large)
                })
            }
        }
    }
}

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注