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

iOS开发 SwiftUI 11:Form

从桌面转移动端要理解,移动端是以界面为核心的,而且是以系统提供的几种界面模式为核心的。创建窗口、显示、隐藏?不存在的。你提供大致的描述,系统决定如何显示和操作。

Form提供层次结构,适合显示多项数据和提供输入。

目录

基本Form和Section

通常用于Form的组件

Toggle开关

监控变化 修改系统显示模式

Picker

ForEach警告:Non-constant range: argument must be an integer literal

使用复杂视图

Picker样式

.automatic .menu

.inline

.navigationLink

.palette .segmented

.wheel

Stepper

Slider


基本Form和Section

示例代码:

Form{ Text("Hello, World! 1") Text("Hello, World! 2") Section(header: Text("段落"), footer: Text("这是一个段落")){ Text("Hello, World! 3") Text("Hello, World! 4") Text("Hello, World! 5") } //.background(.black) }

代码在一个Form里放了5个文本,前两个独立,后三个组合在段落里(Section),并且给段落设置了头尾,效果:

看得出来,Form下默认是一个段落,每个段落格式类似List。代码中屏蔽了给段落设置背景的代码,如果启用,效果是这样的:

晕倒……移动端的行为非常不确定,缺乏内在逻辑。

通常用于Form的组件

表单内通常使用Toggle、Picker、Stepper、Slider等组建来展示数据,不过,Form只是一种布局形式,这些组件大都可以独立使用(当然,需要嵌套在合适的结构里获得合理的布局,所以,还是套在Form里比较简单)。

Toggle开关

toggle是非常常用的,关联到一个开关变量:

@State var b = false var body: some View { Form{ Text(b ? "true" : "false") Section(header: Text("段落"), footer: Text("这是一个段落")){ Toggle(isOn:$b){Text("b")} } //.background(.black) } }

效果:

点一下开关:

监控变化 修改系统显示模式

当开关切换的时候我们通常希望立即发生点什么,一般我们这么写:

@State var b = false @State var str = "info" var body: some View { Form { Text(b ? "true" : "false") Text(str) Section(header: Text("段落"), footer: Text("这是一个段落")) { Toggle(isOn: $b) { Text("b") } .preferredColorScheme(b ? .dark : .light) .onChange(of: b) {if b { str = "TRUE" } else { str = "FALSE" }} } //.background(.black) } //.preferredColorScheme(b ? .dark : .light) //.onChange(of: b) { if b { str = "TRUE" } else { str = "FALSE" } } }

我们增加了一个Text用来在onChange里面做动作,同时还用了preferredColorScheme来修饰系统显示模式为亮色或暗色。看得出来,这两个代码都直接传入了变量b为参数,合理猜测这两个代码其实和Toggle没有任何依赖性,将这两句移到Form后面也是一样工作的。

下面就是放在Form后的效果:

点击一次:

再点击一次:

这两句放在Section后面也是一样的。

Picker

picker用来从列表中选择一个,其功能类似桌面的List或ComboBox。使用方法有点不够高大上,因为它只能返回(关联)选中的项目的索引号。

示例代码:

var items = ["item1","item2","item3"] @State var selectedNumber = 0 //Form里面 Text("\(selectedNumber)") Picker("选择",selection: $selectedNumber) { Text("1") Text("2").tag(2) Text("3") ForEach(0 ..< items.count) { i in Text(items[i]) //.tag(i+10) } }

初始效果:

注意每个项的索引相当诡异,我们初始设置的选中项索引是0,一共有6个选项,前三个手工编写(其中一个用.tag做了指定),后三个由ForEach生成,具体情况是:

  • 默认选项是第一个,如果没有ForEach生成的三个,显示的选中项是第一个
  • 如果没有ForEach生成的三个,手工生成的只有第二个可以被选中,因此,手工生成的选项如果不用tag指定的话是没有索引的
  • 用ForEach生成的项目没有指定tag也会自动生成,但是是从0开始的,因此和前面手工指定的冲突(显示的选中项为同一个tag的第一个项目)
  • 如果启用指定tag的那一句,效果会比较好

ForEach警告:Non-constant range: argument must be an integer literal

现代化编程语言因为动态过程太多而无法用自然的方式跟踪原始数据,因此必须明确指出如何识别数据(如果确信数据不会在运行中更改可以暂时无视这个警告)。

解决方案是明确指出ID:

ForEach(0 ..< items.count, id:\.self)

其实.self就是指针。一个谎言需要用十个谎言来弥补。

使用复杂视图

原则上Picker的条目可以非常复杂,改写一下ForEach循环部分:

ForEach(0 ..< items.count, id:\.self) { i in HStack{ Image("pic2").resizable().frame(height:40) Text(items[i]) }.tag(i+10) }

增加了一个图片,同时希望限制图片的大小(原图很大):

注意顶部的图片就是“Image("pic2").resizable().frame(height:40)”的显示效果,Picker显示的时候图片已经缩小,但是当我们选中之后,主界面却无法直视:

加上.scaledToFit()也没什么用。实际上Image的修饰无效,压根不应该在Picker里面使用复杂图片,而是使用图标,比如换成系统图标:

Image(systemName: "square.and.arrow.up")

无需修饰,效果:

Picker样式

样式一共有这么多:

//.pickerStyle(.automatic) //.pickerStyle(.inline) //.pickerStyle(.menu) //.pickerStyle(.navigationLink) //.pickerStyle(.palette) //.pickerStyle(.segmented) .pickerStyle(.wheel)

以上代码修饰Picker,如果同时写了多个,起作用的是第一个。

.automatic .menu

菜单式是默认方式,就是前面看到的样式。当然,按照说法,默认方式是根据上下文情况来的。

.inline

inline就是桌面的ListBox,如下图:

选中项屁股后面会打勾。

.navigationLink

这个暂时不可用,因为内容不合规:

A picker style represented by a navigation link that presents the options by pushing a List-style picker view.

.palette .segmented

效果:

.wheel

这个轮子很流行,但是其实交互上不理想(没法子,谁让现在是美工主导世界呢):

Stepper

步进器也挺简单,基本代码:

@State var selectedNumber = 0 //Form内 Stepper("selectedNumber \(selectedNumber)",value:$selectedNumber)

效果就是这个样:

还可以复杂一点,设定范围和步长:

Stepper("selectedNumber \(selectedNumber)", value: $selectedNumber,in:0 ... 12,step:1)

但是注意,设置的范围in仅仅用来控制界面,并不影响直接设置关联的变量,比如再增加一个不限制范围的控件:

Stepper("selectedNumber \(selectedNumber)", value: $selectedNumber,in:0 ... 12,step:1) Stepper("selectedNumber \(selectedNumber)", value: $selectedNumber)

上面一个到达12的时候:

加号无效了,不可以继续点击。但下面的仍然可以:

上面的控件已经突破了范围限制,而且加号又可用了,我们再点一下上面的控件的加号:

又自动跳回到上限了。

Slider

滑块也很简单:

@State var floatValue : Float = 0 //Form内 Slider(value: $floatValue,in: 1 ... 10,step: 2)

效果:

为了显示数值,要自己加工一下:

HStack{ Text("Slider \(floatValue)") Slider(value: $floatValue,in: 1 ... 10,step: 2) }

效果:

绑定值预设是0,初始显示0,动过以后就不能再突破限制,这和Stepper的行为是一样的。

如果需要竖向滑块,按照视图的常规做法:

HStack{ Text("Slider \(floatValue)") Slider(value: $floatValue,in: 1 ... 10,step: 2) .rotationEffect(.degrees(-90)) .frame(height: 200) }

效果:

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

相关文章:

  • Gemma-4 E4B:如何用4.5B参数实现多模态智能革命?
  • 如何用FXTest实现高效接口测试:10个实用技巧提升测试效率
  • 7天掌握Sulphur-2-Base-GGUF:AI视频生成的终极免费解决方案
  • BubbleTabBar实战:打造现代化电商应用的动感导航体验
  • Dokemon存储管理终极指南:卷和绑定挂载的最佳实践
  • SAM-Audio音频分离革命:用自然语言精准提取任何声音
  • YOLO26改进策略【Neck】| ASF-YOLO 注意力尺度序列融合模块改进颈部网络,提高小目标检测精度
  • 软考:高级软件架构师学习笔记----嵌入式技术
  • HyperDB扩展性设计:前缀trie算法的实现原理
  • BubbleTabBar动画效果:创建令人惊艳的交互体验
  • LD2410雷达传感器库核心技术深度解析:如何实现24GHz FMCW雷达的高精度人体检测方案
  • 3个关键步骤掌握tiktoken:OpenAI模型的高性能分词器解决方案
  • Buzz:完全离线的智能音频转录工具,让语音转文字变得简单高效
  • Jeepay计全支付:5分钟掌握企业级支付系统的部署与使用
  • VIA键盘配置深度解析:从核心功能到高效定制的专业技巧
  • Self-Parking Car Evolution深度解析:3D物理模拟与进化算法结合
  • 新能源汽车DC/DC变换器测试作业指导书
  • iOS开发 SwitfUI 12:颜色和颜色选择器 RGB转换
  • 钱条:可视化你的收入进度,让每一分努力都看得见
  • ENFUGUE故障排除:解决10个最常见的安装与运行问题
  • elasticsearch学习笔记(十)——Elasticsearch横向扩容过程与容错机制
  • 动漫TrackerList终极指南:如何通过智能Tracker优化实现动漫资源下载加速300%
  • 端云协同代码辅助:用Gemma 2B轻量模型破解Claude配额瓶颈
  • Agent Skill 是什么?不是保存 Prompt,而是 Agent 的可复用能力包
  • 10分钟快速上手Self-Parking Car Evolution:浏览器中的AI进化模拟
  • 不从众,方破局:从越南摩托溃败、张雪WSBK封神,看懂新能源研发的真正坚守
  • gocryptfs终极指南:Go语言加密文件系统的完整解决方案
  • Stout高级功能:支持客户端路由器的SPA应用部署技巧
  • 群晖NAS硬盘兼容性终极解决方案:Synology HDD db工具完全指南
  • 5分钟掌握poi-tl:企业文档自动化的终极解决方案