当前位置: 首页 > news >正文

SwiftUI学习笔记4-按钮

本节内容

  • 创建和自定义按钮
  • 使用闭包执行代码
  • 尾随闭包语法
  • 使用@State属性更新视图
  • 使用ForEach创建动态数量的视图。

创建具有状态视图,并学习如何使用按钮使应用程序具有交互性。

用@State属性和按钮创建虚拟骰子

通常建议将@state声明为 private 或 fileprivate。这样做的原因是:

封装性:将状态变量限制在声明的结构体或视图中,可以防止外部组件意外修改这个状态,从而更好地维护数据的一致性。

明确性:使用 private 可以提高代码的可读性,指示该状态是内部使用的,不应从其他地方访问。

SF Symbols提供了骰子的所有面

.resizable修饰符表示图像可以伸展以填充任何空间

.使图像能够根据指定的宽度和高度进行缩放。这意味着当你给图像指定一个框架(如 .frame(width: 100, height: 100))时,图像将根据这个框架来调整其大小

编辑了一个骰子和按钮,让骰子的图片随着数字随机改变而改变。

// DiceView.swift // DiceRoller // // Created by sakiko on 2026/4/23. // import SwiftUI struct DiceView: View { var body: some View { VStack { //包装在VStack里方便添加按钮 var numberOfPips : Int = 1 //表示骰子点数 Image(systemName: "die.face.\\(numberOfPips)") .resizable() .frame(width: 100, height: 100) Button("Roll") { //添加按钮,按钮名字叫"Roll" numberOfPips = Int.random(in: 1...6)//控制numberOfPip为随机生成 } } } } #Preview { DiceView() }
在用户点击 Roll 按钮且numberOfPips属性变化时更新图像。

@State:SwiftUI不会监控应用中的每个属性变化,所以需要使用@State来标记属性,告诉它要随时更新UI

在@State里定义要变化的属性,就可以实现实时更新UI的效果

点击Roll,骰子随机变化

struct DiceView: View { @State private var numberOfPips: Int = 1//表示骰子点数 var body: some View { VStack { //包装在VStack里方便添加按钮 Image(systemName: "die.face.\\(numberOfPips)") .resizable() .frame(width: 100, height: 100) Button("Roll") { //添加按钮,按钮名字叫"Roll" numberOfPips = Int.random(in: 1...6)//控制numberOfPip为随机生成 } } } }

使用.buttonStyle(.bordered)来为按钮添加边框

使用withAnimation来动画化,让图像切换更加平滑。

动态显示骰子

.largeTitle.有很多形式,其中一个是lowercaseSmallCaps

这里有很多不同的对齐方式,等有用到的需求时候再看吧

VStack { Text("Dice Roller") .font(.largeTitle.lowercaseSmallCaps()

通过添加实例的方式添加元素到控制显示的页面

// // ContentView.swift // DiceRoller // // Created by sakiko on 2026/4/23. // import SwiftUI struct ContentView: View { var body: some View { VStack { Text("Dice Roller") .font(.largeTitle.lowercaseSmallCaps()) HStack { DiceView()//通过添加实例的方式来添加显示的元素 DiceView() DiceView() } } .padding() } } #Preview { ContentView() }

ForEach的使用:显示任意数量的骰子

ForEach在数量不定的时候使用,它可以规定显示多少个实例,最下面表示要显示的实例。

struct ContentView: View { var body: some View { VStack { Text("Dice Roller") .font(.largeTitle.lowercaseSmallCaps()) HStack { ForEach(1...3, id: \\.description) { _ in DiceView()} } } .padding() } } #Preview { ContentView() }

为骰子数量添加属性,并用带有闭包的按钮控制骰子的增加和减少

HStack { Button("移除骰子") { numberOfDice -= 1 //这是一个闭包,控制骰子总数 } Button("添加骰子") { numberOfDice += 1//同上 } }

因为没有限制在值为1时禁用功能,所以在移除到0的时候会炸。

添加禁用功能.disabled(numberOfDice == 1)

本质是利用布尔值,当 == 1的时候,修饰符会禁用按钮。

// // ContentView.swift // DiceRoller // // Created by sakiko on 2026/4/23. // import SwiftUI struct ContentView: View { @State private var numberOfDice: Int = 1 var body: some View { VStack { Text("Dice Roller") .font(.largeTitle.lowercaseSmallCaps()) HStack { ForEach(1...numberOfDice, id: \\.description) { _ in DiceView()} } } HStack { Button("移除骰子") { withAnimation{ numberOfDice -= 1 //这是一个闭包,控制骰子总数 }//.disabled控制禁用 } .disabled(numberOfDice == 1) Button("添加骰子") { withAnimation{//动画化增加骰子 numberOfDice += 1//同上 } } .disabled(numberOfDice == 3) } .buttonStyle(.bordered) .padding() } } #Preview { ContentView() }

效果如下:

调整界面,使用更多骰子

当有四个或五个骰子时,它们会垂直拉伸,因为HStack上下没有东西来限制其高度。为防止这种情况,将骰子图片的宽高比设置为 1

这是修改前:

VStack { //包装在VStack里方便添加按钮 Image(systemName: "die.face.\\(numberOfPips)") .resizable() .frame(width: 100, height: 100)//使用.resizable和maxwidth

修改后:

.aspectRatio(1, contentMode: .fit)表示如果图像的宽高比和可用空间不一致,会缩小到比较小的轴。并在另一边留下空白空间。

VStack { //包装在VStack里方便添加按钮 Image(systemName: "die.face.\\(numberOfPips)") .resizable() .frame(maxWidth: 100, maxHeight: 100) .aspectRatio(1, contentMode: .fit)

左边为未添加的状态,右边为添加后的状态

在按钮标签中使用图像

按钮必须要有文本标签,即使它未必显示成对应的内容(比如显示成图片)

为Button添加参数,来添加显示在按钮上的图片

HStack { Button("移除骰子", systemImage: "minus.circle.fill") { withAnimation{ numberOfDice -= 1 //这是一个闭包,控制骰子总数 }//.disabled控制禁用 } .disabled(numberOfDice == 1) //为骰子button添加另一个参数,以添加图像,上面也是这样 Button("添加骰子", systemImage: "plus.circle.fill") { withAnimation{//动画化增加骰子 numberOfDice += 1//同上 } }

使用.labelStyle(.iconOnly)修饰符来隐藏按钮文本

.buttonStyle(.bordered) .padding() .labelStyle(.iconOnly)//隐藏按钮文本 .font(.title)//增加按钮大小

设置背景颜色与样式

.padding() .background(Color("AppBackground"))//为ContentView body里全部内容的背景 .tint(.white)

.tint修饰符:仅影响依赖强调色的视图,设置成白色

利用.foregroundStyle(.white) 将文本前景色设置为白色,效果如下

使用frame将背景扩展,这是上一课的内容。

将.infinity参数传递给maxWidth和maxHeight,让框架在两个方向尽可能扩展

使用填充变体,改变图片的前景色来改变图片样式。

可以使用两种前景色,一种主色一种次色

DiceView.swift

struct DiceView: View { @State private var numberOfPips: Int = 1//表示骰子点数 var body: some View { VStack { //包装在VStack里方便添加按钮 Image(systemName: "die.face.\\(numberOfPips).fill")//.fill是填充变体的意思 .resizable() .frame(maxWidth: 100, maxHeight: 100) .aspectRatio(1, contentMode: .fit) .foregroundStyle(.black, .white)//将骰子前景色设置为白色

有些 SF Symbols 是按层级组合的,包含可以呈现主色、次色和三级色的不同元素。有四种 SF Symbol 渲染模式,你可以使用.symbolRenderingMode修饰符手动设置。关于渲染模式以及更多关于 SF Symbols 的信息

效果如下:

Button的模式

Button("AddOne"){ counterValue += 1 }

练习:

  1. 将 Roll 按钮的按钮边框形状更改为胶囊形。

  2. 使用.symbolRenderingMode自定义移除骰子和添加骰子按钮。

  3. I不要在骰子图像和其下方的 Roll 按钮下方放置图像,而是修改DiceView以便人们可以直接点击骰子本身来随机化它。

struct DiceView: View { @State private var numberOfPips: Int = 1//表示骰子点数 var body: some View { VStack { //包装在VStack里方便添加按钮 Image(systemName: "die.face.\\(numberOfPips).fill")//.fill是填充变体的意思 .resizable() .frame(maxWidth: 100, maxHeight: 100) .aspectRatio(1, contentMode: .fit) .foregroundStyle(.black, .white)//将骰子前景色设置为白色 Button("摇一摇") { //添加按钮,按钮名字叫"Roll" withAnimation{//使图像变换平滑化,这本质是尾随闭包 numberOfPips = Int.random(in: 1...6)//控制numberOfPip为随机生成 } }.frame(width: 100, height: 32) //在Button的外面创建一个框架框柱它, .cornerRadius(16) .buttonStyle(.bordered)

.frame(width: 100, height: 32) //在Button的外面创建一个框架框柱它

.cornerRadius(16)创建这个框架的圆角

.buttonStyle(.bordered) 将按钮的样式设置为“边框样式”

.symbolRenderingMode主要有两个模式:

  1. .monochrome:以单一颜色渲染符号,通常用来使符号与背景颜色对比明显,适合于简洁的设计。
  2. .hierarchical:使用层次化的颜色渲染符号,适合用于强调某个层级关系,显示更多的细节。
HStack { Button("移除骰子", systemImage: "minus.circle.fill") { withAnimation{ numberOfDice -= 1 //这是一个闭包,控制骰子总数 }//.disabled控制禁用 } .disabled(numberOfDice == 1) .symbolRenderingMode(.hierarchical) //为骰子button添加另一个参数,以添加图像,上面也是这样 Button("添加骰子", systemImage: "plus.circle.fill") { withAnimation {//动画化增加骰子 numberOfDice += 1//同上 }

3.怎么在按钮里写图片:这个还不会。标记一下

现在是把按钮放的很大,但是图片还是原来的大小

struct DiceView: View { @State private var numberOfPips: Int = 1//表示骰子点数 var body: some View { VStack { //包装在VStack里方便添加按钮 Button("摇一摇", systemImage: "die.face.\\(numberOfPips).fill")//.fill是填充变体的意思 {withAnimation{//使图像变换平滑化,这本质是尾随闭包 numberOfPips = Int.random(in: 1...6)//控制numberOfPip为随机生成 } } .labelStyle(.iconOnly) .frame(width: 200, height: 200) .aspectRatio(1, contentMode: .fit) .foregroundStyle(.black, .white) .font(.title) .aspectRatio(contentMode: .fill) } } }

只能这样了…

http://www.jsqmd.com/news/694434/

相关文章:

  • AMD Ryzen 处理器功耗调校终极实战:RyzenAdj 完整指南
  • 别再傻傻分不清了!Qt状态栏addPermanentWidget、addWidget、showMessage到底谁覆盖谁?
  • 【T5模型架构】从Transformer到T5:架构演进与核心模块拆解
  • 5分钟上手Zotero-Style:让文献管理焕然一新的终极美化插件
  • 《2026年必看:六款热门AI编程工具横评》
  • 线程安全崩塌,连接池雪崩,序列化溢出——C++ MCP网关5大致命报错全解析,附GDB+eBPF精准诊断模板
  • Skywalking存储引擎选择:MySQL vs ElasticSearch vs H2,哪个更适合你?
  • 告别审查:Windows XP系统运行GoodbyeDPI的兼容性挑战与解决方案
  • 2026年版|大模型算法工程师必看!6大核心方向优先级排序(建议收藏)
  • 30天快速上手Python-01Anaconda 安装
  • 蓝牙实战解析:定向广播ADV_DIRECT_IND的连接建立与占空比策略
  • Cadence Virtuoso仿真报错‘No convergence’?别慌,手把手教你调大reltol和减小gmin
  • 别再为IPsec隧道‘单向通’头疼了!手把手教你排查FortiGate双端互连失败(附实战截图)
  • 如何让微信聊天记录成为你的永久数字资产?本地工具WeChatMsg完全指南
  • 别只会说“Thank you”:用ChatGPT润色你的SCI回复信,让语气更地道
  • 手把手教你用face_recognition和Flask,30分钟搭建一个Web版人脸识别系统(Python 3.10+)
  • VSCode实时协作配置失效的7个隐秘原因:从WebSocket超时到权限链断裂的全链路诊断手册
  • WarcraftHelper:魔兽争霸3现代优化终极指南
  • 【学习笔记】车道线识别——图像处理方法
  • Vue Design System:从零开始构建企业级UI设计系统的完整指南
  • 2025年黑苹果装机终极指南:gh_mirrors/ha/Hackintosh项目完全解析
  • paho.mqtt.c与主流MQTT代理集成:Mosquitto、EMQX、HiveMQ实战
  • x-flux IP-Adapter应用实战:实现图像提示生成的高效方法
  • 避坑指南:Win11下用VS2022配置PCL1.12.1,环境变量和VTK警告都帮你搞定了
  • 终极指南:如何用12-Factor Agents构建革命性教育科技个性化学习体验
  • 从CentOS迁移者视角:手把手在VMware上安装openEuler 22.03 LTS SP3并配置中文环境
  • 【收藏级】月薪6万招不到人!2026年AI时代红利,小白程序员必看
  • 【仅限政企开发者】:VSCode国产化调试证书链信任体系重构方案——基于国家密码管理局SM2根证书的100%自主可控调试通道搭建
  • Linux内核模块/CUDA驱动/RT-Thread组件开发必读:2026内存安全编码黄金11条(附LLVM Pass验证源码)
  • emailjs 与其他邮件库对比:为什么选择 emailjs 的6大理由