SwiftUI 简明教程之文本与图片
本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容。
Eul 是一款 SwiftUI & Combine 教程类 App(iOS、macOS),以文章(文字、图片、代码)配合真机示例(Xcode 12+、iOS 14+,macOS 11+)的形式呈现给读者。笔者意在尽可能使用简洁明了的语言阐述 SwiftUI & Combine 相关的知识,使读者能快速掌握并在 iOS 开发中实践。
Text
本地化字符串
SwiftUI 中涉及到字符串的地方,基本都支持普通的字符串和本地化字符串。Text 的初始化方法也不例外:
/// 普通字符串
init<S>(_ content: S) where S : StringProtocol
/// 本地化字符串
init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil)
我们先创建多语言文件,分别写入中英文的 Stay Hungry, Stay Foolish! 文本,通过枚举去获取对应的 LocalizedStringKey,然后就可以使用 Text(LocalizeKey.Hungry) 方便地展示本地化字符串了。
enum LocalizeKey {
static let kHungry: LocalizedStringKey = "Hungry"
}
struct LocalizableView: View {
var body: some View {
Text(LocalizeKey.kHungry)
}
}
// "Hungry" = "Stay Hungry, Stay Foolish!";
// "Hungry" = "求知若饥,虚心若愚!";
富文本
Text 实现了操作符重载,我们可以直接用 + 来拼接不同样式的文字。
struct RichTextView: View {
private let text: Text =
Text("Stay ").foregroundColor(.blue).font(.title).italic() +
Text("Hungry, ").font(.headline) +
Text("Stay ").foregroundColor(.red).font(.title) +
Text("Foolish!").font(.headline).underline()
var body: some View {
text
}
}
另外,Text 本身遵循 Equatable 协议,我们还可以直接使用 == 和 != 来对两个 Text 进行判等。
日期
Text 甚至可以直接展示日期,现在创建一个倒计时控件只需要一行代码就可以实现!
Text 的初始化方法有如下几种:
/** 以下日期均指当地日期 */
/// 使用指定样式展示日期
public init(_ date: Date, style: Text.DateStyle)
/// 展示日期范围
public init(_ dates: ClosedRange<Date>)
/// 展示日期间隔
public init(_ interval: DateInterval)
DateStyle 有如下枚举值:
public struct DateStyle {
/// 时间,比如:11:23PM
public static let time: Text.DateStyle
/// 日期,比如:June 3, 2019
public static let date: Text.DateStyle
/// 相对现在的时间,比如:2 hours, 23 minutes
public static let relative: Text.DateStyle
/// 与现在的时间差,比如:-3 months,+2 hours
public static let offset: Text.DateStyle
/// 倒计时,比如:36:59:01
public static let timer: Text.DateStyle
}
下面我们通过代码展示其用法:
struct DateView: View {
private var future: Date { now.addingTimeInterval(3600) }
private var now: Date { Date() }
var body: some View {
VStack(alignment: .leading, spacing: 10) {
row(style: ".date") { Text(now, style: .date) }
row(style: ".offset") { Text(future, style: .offset) }
row(style: ".relative") { Text(future, style: .relative) }
row(style: ".time") { Text(future, style: .time) }
row(style: ".timer") { Text(future, style: .timer) }
row(style: "Range") { Text(now...future) }
row(style: "Interval") { Text(DateInterval(start: now, end: future)) }
}
}
func row<Content: View>(style: String, @ViewBuilder content: () -> Content) -> some View {
VStack {
HStack {
content()
Spacer()
Text(style).foregroundColor(.secondary)
}
Divider ()
}
}
}
先简述一下 @ViewBuilder 的作用:它可以用来修饰闭包参数,并从中构建视图。
.offset、.relative 和.timer 展示的时间都是根据秒数变化的,其它样式的日期则是静态的。
Label
构建方法
Label 是一个相当强大的控件,可以快速生成图片和文字的组合,默认布局是左图右文,也支持自定义配置。
它有如下初始化方法:
init<S>(S, image: String)
init<S>(S, systemImage: String)
init(LocalizedStringKey, image: String)
init(LocalizedStringKey, systemImage: String)
// Title: View, icon: View
init(title: () -> Title, icon: () -> Icon)
我们试着用以上方法构建不同的视图,代码和界面如下:
Label("Swift", systemImage: "swift")
.foregroundColor(.orange)
Label(
title: {
Text("Apple")
},icon: {
Image(systemName: "applelogo")
}
)
.foregroundColor(.blue)
Label(
title: {
Image(systemName: "gift.fill")
.renderingMode(.original)
},icon: {
Text("Gift")
}
)
.foregroundColor(.red)
.labelStyle(TitleOnlyLabelStyle())
LabelStyle 有如下三种样式:
- DefaultLabelStyle // Title + Icon
- IconOnlyLabelStyle // 只显示 Icon
- TitleOnlyLabelStyle // 只显示 Title
自定义样式
上面的构建方法中,其实还有一种是未曾提及的:
init(LabelStyleConfiguration)
LabelStyleConfiguration 是一个结构体类型,包含 Icon 和 Title。
我们可以通过这个初始化方法,给系统提供的样式添加自定义的样式。
比如我们需要给 Label 加上阴影,可以先创建一个遵循 LabelStyle 协议的 ShadowLabelStyle,然后使用该样式。下面代码中的 Configuration 实际上就是 LabelStyleConfiguration ,只不过系统通过 typealias Configuration = LabelStyleConfiguration 改头换面了而已。
Label("Apple", systemImage: "applelogo")
.labelStyle(ShadowLabelStyle())
struct ShadowLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
Label(configuration)
.shadow(color: Color.black.opacity(0.5), radius: 5, x: 0, y: 5)
}
}
上面的样式有一定的局限性,如果我们需要一个垂直布局或是左右对齐的样式呢?实现的原理是一样的,代码如下:
struct VerticalLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
VStack(alignment: .center, spacing: 10) {
configuration.icon
configuration.title
}
}
}
struct LeftRightLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .center, spacing: 10) {
configuration.icon
Spacer()
configuration.title
}
}
}
TextField
TextField 有如下的三种构建方式:
- 普通的初始化方法
- 在方法 1 的基础上新增了监听功能,可以监听编辑状态、Return 键的按下动作
- 在方法 2 的基础上新增了格式转换功能
前两种方法比较简单,这里说一下方法 3 的细节。如下样例是将输入的文字转换成数字,当我们正在输入的时候,格式转换功能是不生效的,只有当编辑结束的时候,才会去执行转换,如果转换成功,会更新绑定的值(s3),如果转换失败,不会更新 s3。
@State private var s1 = ""
@State private var s2 = ""
@State private var s3 = 0
@State private var pwd = ""
GroupBox(label: Text(s1)) {
/// 1
TextField("TextField", text: $s1)
}
GroupBox(label: Text(s2)) {
/// 2
TextField("Observe TextField", text: $s2) { (isEditing) in
print(isEditing)
} onCommit: {
print("Return")
}
.textFieldStyle(RoundedBorderTextFieldStyle())
}
GroupBox(label: Text(String(s3))) {
/// 3
TextField("Formatter TextField", value: $s3, formatter: NumberFormatter()) {
(isEditing) in
print(isEditing)
} onCommit: {
print("Return")
}
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numbersAndPunctuation)
}
GroupBox(label: Text("密码输入: \(pwd)")) {
/// 密码输入
SecureField("Password", text: $pwd)
}
TextEditor
TextEditor 的使用比较简单,如下代码我们就可以轻松创建一个文本输入框:
@State private var text = "Stay Hungry, Stay Foolish!"
TextEditor(text: $text)
.frame(height: 150)
.lineSpacing(10.0) // 行距
.multilineTextAlignment(.center) // 对齐方式
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.blue, lineWidth: 1)
)
系统并没有提供 placeholder 这样的特性,不过我们可以轻松实现这个功能。
// 添加 placeholder
ZStack(alignment: .topLeading) {
TextEditor(text: $text2)
.frame(height: 150)
.border(Color.blue)
if text2.isEmpty {
Text("Type something")
.foregroundColor(Color(UIColor.placeholderText))
.padding(8)
}
}
Image
Image 用来展示图片,它可以加载资源包中的图片文件和系统内置的图标(SF Symbols)。
以下是几点提示:
- 图片默认具有伸展特性,
.resizable()可使图片不超出屏幕或指定区域 - 如果要展示 SF Symbols 内置图标自带的颜色,可以用
.renderingMode(.original).来渲染。 - Text 可以直接插入图片,支持字符串插值和拼接两种方式
本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容。
SwiftUI 简明教程之文本与图片的更多相关文章
- SwiftUI 简明教程之指示器
本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容. Eul 是一款 SwiftUI & Combine 教程 App(iOS.macOS),以文章(文字.图片. ...
- SwiftUI 简明教程之自定义 Modifier
本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容. Eul 是一款 SwiftUI & Combine 教程 App(iOS.macOS),以文章(文字.图片. ...
- SwiftUI 简明教程之属性包装器
本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容. Eul 是一款 SwiftUI & Combine 教程 App(iOS.macOS),以文章(文字.图片. ...
- [深度应用]·实战掌握PyTorch图片分类简明教程
[深度应用]·实战掌握PyTorch图片分类简明教程 个人网站--> http://www.yansongsong.cn/ 项目GitHub地址--> https://github.com ...
- HTML简明教程(二)
HTML简明教程(二) 一.HTML 图像 二.HTML 表格 三.HTML 列表 四.HTML div和 span 五.HTML 布局 六.HTML 表单和输入 七.HTML 框架 八.HTML内联 ...
- HTML简明教程(一)
HTML简明教程(一) 内容主体来自:W3School 一.HTML 简介 二.HTML 基础 三.HTML 元素 四.HTML 属性 五.HTML 标题 六.HTML 段落 七.HTML 文本格式化 ...
- CSDN Markdown简明教程3-表格和公式
0. 文件夹 文件夹 前言 表格 1 表格 2 表格对齐方式 公式 1 行内公式 2 陈列公式displayed formulas 3 MathJax语法 深入 声明 1. 前言 Markdown是一 ...
- CSDN Markdown简明教程4-UML画画
0.文件夹 文件夹 前言 序列图 1 序列图演示样例 2 序列图语法 流程图 1 流程图演示样例 2 流程图语法 节点定义 节点连接 Gravizo 声明 1. 前言 Markdown是一种轻量级的标 ...
- Vbs 脚本编程简明教程之一
—为什么要使用 Vbs ? 在 Windows 中,学习计算机操作也许很简单,但是很多计算机工作是重复性劳动,例如你每周也许需要对一些计算机文件进行复制.粘贴.改名.删除,也许你每天启动 计算机第一件 ...
随机推荐
- elasticsearch如何设计索引
本文为博客园作者所写: 一寸HUI,个人博客地址:https://www.cnblogs.com/zsql/ 最近在做es相关的工作,所以记录下自己的一些想法,可能很多方面不会很全面,但是基本都是经过 ...
- django学习-23.admin管理后台的数据表数据的自定义展示
目录结构 1.前言 2.自定义设置一张指定的数据表的列表展示内容 2.1.第一步:如果我们想让数据表[hello_person]里面的表字段值全部展示出来,需在应用[hello]里的[admin.py ...
- 01.Numpy数组的基本应用
数组的创建 数组的访问 数组的合并 数组的分割 数组创建 >>> import numpy as np 创建一维数组 >>> x = np.arange(10) & ...
- 一文读懂Servlet
1 Servlet简介 Servlet就是sun公司开发动态web的一门技术 Sun在这些API中提供一个接口叫做:Servlet. 开发一个Servlet程序,只需两步: 编写一个类,实现Servl ...
- Google单元测试框架gtest之官方sample笔记1--简单用例
1.0 通用部分 和常见的测试工具一样,gtest提供了单体测试常见的工具和组件.比如判断各种类型的值相等,大于,小于等,管理多个测试的测试组如testsuit下辖testcase,为了方便处理初始化 ...
- Spring-03 依赖注入(DI)
Spring-03 依赖注入(DI) 依赖注入(DI) 依赖注入(Dependency Injection,DI). 依赖 : 指Bean对象的创建依赖于容器,Bean对象的依赖资源. 注入 : 指B ...
- SpringBoot 配置文件以及依赖 加上跨域配置文件
配置目录: application.properties的配置 #设置服务端口号 server.port = 8090 #配置数据源 spring.datasource.driver-class-na ...
- Cassandra数据操作管理工具tableplus
一.概述 Cassandra是一个NoSQL数据库,具有类SQL CQL入口,基本语法与SQL保持一致.其实笔者认为 Cassandra的自带的cqlsh已经满足本的需求:如: 但是用习惯了数据库操作 ...
- 六. SpringCloud网关
1. Gateway概述 1.1 Gateway是什么 服务网关还可以用Zuul网关,但是Zuul网关由于一些维护问题,所以这里我们学习Gateway网关,SpringCloud全家桶里有个很重要的组 ...
- 使用jsoup十分钟内掌握爬虫技术
对,就是十分钟,没有接触过爬虫的你,肯定一脸懵逼,感觉好高深的样子,一开始我也有点懵,但用了以后发现还是很简单的,java爬虫框架有很多,让我有种选择困难症,通过权衡比较还是感觉jsoup比较好用些, ...