经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
SwiftUI 简明教程之文本与图片
来源:cnblogs  作者:Bruce2077  时间:2021/4/19 8:54:12  对本文有异议

本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容。

Eul 是一款 SwiftUI & Combine 教程类 App(iOS、macOS),以文章(文字、图片、代码)配合真机示例(Xcode 12+、iOS 14+,macOS 11+)的形式呈现给读者。笔者意在尽可能使用简洁明了的语言阐述 SwiftUI & Combine 相关的知识,使读者能快速掌握并在 iOS 开发中实践。

Text

本地化字符串

SwiftUI 中涉及到字符串的地方,基本都支持普通的字符串和本地化字符串。Text 的初始化方法也不例外:

  1. /// 普通字符串
  2. init<S>(_ content: S) where S : StringProtocol
  3. /// 本地化字符串
  4. init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil)

我们先创建多语言文件,分别写入中英文的 Stay Hungry, Stay Foolish! 文本,通过枚举去获取对应的 LocalizedStringKey,然后就可以使用 Text(LocalizeKey.Hungry) 方便地展示本地化字符串了。

  1. enum LocalizeKey {
  2. static let kHungry: LocalizedStringKey = "Hungry"
  3. }
  4. struct LocalizableView: View {
  5. var body: some View {
  6. Text(LocalizeKey.kHungry)
  7. }
  8. }
  9. // "Hungry" = "Stay Hungry, Stay Foolish!";
  10. // "Hungry" = "求知若饥,虚心若愚!";

富文本

Text 实现了操作符重载,我们可以直接用 + 来拼接不同样式的文字。

  1. struct RichTextView: View {
  2. private let text: Text =
  3. Text("Stay ").foregroundColor(.blue).font(.title).italic() +
  4. Text("Hungry, ").font(.headline) +
  5. Text("Stay ").foregroundColor(.red).font(.title) +
  6. Text("Foolish!").font(.headline).underline()
  7. var body: some View {
  8. text
  9. }
  10. }

另外,Text 本身遵循 Equatable 协议,我们还可以直接使用 ==!= 来对两个 Text 进行判等。

日期

Text 甚至可以直接展示日期,现在创建一个倒计时控件只需要一行代码就可以实现!

Text 的初始化方法有如下几种:

  1. /** 以下日期均指当地日期 */
  2. /// 使用指定样式展示日期
  3. public init(_ date: Date, style: Text.DateStyle)
  4. /// 展示日期范围
  5. public init(_ dates: ClosedRange<Date>)
  6. /// 展示日期间隔
  7. public init(_ interval: DateInterval)

DateStyle 有如下枚举值:

  1. public struct DateStyle {
  2. /// 时间,比如:11:23PM
  3. public static let time: Text.DateStyle
  4. /// 日期,比如:June 3, 2019
  5. public static let date: Text.DateStyle
  6. /// 相对现在的时间,比如:2 hours, 23 minutes
  7. public static let relative: Text.DateStyle
  8. /// 与现在的时间差,比如:-3 months,+2 hours
  9. public static let offset: Text.DateStyle
  10. /// 倒计时,比如:36:59:01
  11. public static let timer: Text.DateStyle
  12. }

下面我们通过代码展示其用法:

  1. struct DateView: View {
  2. private var future: Date { now.addingTimeInterval(3600) }
  3. private var now: Date { Date() }
  4. var body: some View {
  5. VStack(alignment: .leading, spacing: 10) {
  6. row(style: ".date") { Text(now, style: .date) }
  7. row(style: ".offset") { Text(future, style: .offset) }
  8. row(style: ".relative") { Text(future, style: .relative) }
  9. row(style: ".time") { Text(future, style: .time) }
  10. row(style: ".timer") { Text(future, style: .timer) }
  11. row(style: "Range") { Text(now...future) }
  12. row(style: "Interval") { Text(DateInterval(start: now, end: future)) }
  13. }
  14. }
  15. func row<Content: View>(style: String, @ViewBuilder content: () -> Content) -> some View {
  16. VStack {
  17. HStack {
  18. content()
  19. Spacer()
  20. Text(style).foregroundColor(.secondary)
  21. }
  22. Divider ()
  23. }
  24. }
  25. }

先简述一下 @ViewBuilder 的作用:它可以用来修饰闭包参数,并从中构建视图。

.offset.relative.timer 展示的时间都是根据秒数变化的,其它样式的日期则是静态的。

Label

构建方法

Label 是一个相当强大的控件,可以快速生成图片和文字的组合,默认布局是左图右文,也支持自定义配置。

它有如下初始化方法:

  1. init<S>(S, image: String)
  2. init<S>(S, systemImage: String)
  3. init(LocalizedStringKey, image: String)
  4. init(LocalizedStringKey, systemImage: String)
  5. // Title: View, icon: View
  6. init(title: () -> Title, icon: () -> Icon)

我们试着用以上方法构建不同的视图,代码和界面如下:

  1. Label("Swift", systemImage: "swift")
  2. .foregroundColor(.orange)
  3. Label(
  4. title: {
  5. Text("Apple")
  6. },icon: {
  7. Image(systemName: "applelogo")
  8. }
  9. )
  10. .foregroundColor(.blue)
  11. Label(
  12. title: {
  13. Image(systemName: "gift.fill")
  14. .renderingMode(.original)
  15. },icon: {
  16. Text("Gift")
  17. }
  18. )
  19. .foregroundColor(.red)
  20. .labelStyle(TitleOnlyLabelStyle())

LabelStyle 有如下三种样式:

  • DefaultLabelStyle // Title + Icon
  • IconOnlyLabelStyle // 只显示 Icon
  • TitleOnlyLabelStyle // 只显示 Title

自定义样式

上面的构建方法中,其实还有一种是未曾提及的:

  1. init(LabelStyleConfiguration)

LabelStyleConfiguration 是一个结构体类型,包含 IconTitle

我们可以通过这个初始化方法,给系统提供的样式添加自定义的样式。

比如我们需要给 Label 加上阴影,可以先创建一个遵循 LabelStyle 协议的 ShadowLabelStyle,然后使用该样式。下面代码中的 Configuration 实际上就是 LabelStyleConfiguration ,只不过系统通过 typealias Configuration = LabelStyleConfiguration 改头换面了而已。

  1. Label("Apple", systemImage: "applelogo")
  2. .labelStyle(ShadowLabelStyle())
  3. struct ShadowLabelStyle: LabelStyle {
  4. func makeBody(configuration: Configuration) -> some View {
  5. Label(configuration)
  6. .shadow(color: Color.black.opacity(0.5), radius: 5, x: 0, y: 5)
  7. }
  8. }

上面的样式有一定的局限性,如果我们需要一个垂直布局或是左右对齐的样式呢?实现的原理是一样的,代码如下:

  1. struct VerticalLabelStyle: LabelStyle {
  2. func makeBody(configuration: Configuration) -> some View {
  3. VStack(alignment: .center, spacing: 10) {
  4. configuration.icon
  5. configuration.title
  6. }
  7. }
  8. }
  9. struct LeftRightLabelStyle: LabelStyle {
  10. func makeBody(configuration: Configuration) -> some View {
  11. HStack(alignment: .center, spacing: 10) {
  12. configuration.icon
  13. Spacer()
  14. configuration.title
  15. }
  16. }
  17. }

TextField

TextField 有如下的三种构建方式:

  1. 普通的初始化方法
  2. 在方法 1 的基础上新增了监听功能,可以监听编辑状态、Return 键的按下动作
  3. 在方法 2 的基础上新增了格式转换功能

前两种方法比较简单,这里说一下方法 3 的细节。如下样例是将输入的文字转换成数字,当我们正在输入的时候,格式转换功能是不生效的,只有当编辑结束的时候,才会去执行转换,如果转换成功,会更新绑定的值(s3),如果转换失败,不会更新 s3。

  1. @State private var s1 = ""
  2. @State private var s2 = ""
  3. @State private var s3 = 0
  4. @State private var pwd = ""
  5. GroupBox(label: Text(s1)) {
  6. /// 1
  7. TextField("TextField", text: $s1)
  8. }
  9. GroupBox(label: Text(s2)) {
  10. /// 2
  11. TextField("Observe TextField", text: $s2) { (isEditing) in
  12. print(isEditing)
  13. } onCommit: {
  14. print("Return")
  15. }
  16. .textFieldStyle(RoundedBorderTextFieldStyle())
  17. }
  18. GroupBox(label: Text(String(s3))) {
  19. /// 3
  20. TextField("Formatter TextField", value: $s3, formatter: NumberFormatter()) {
  21. (isEditing) in
  22. print(isEditing)
  23. } onCommit: {
  24. print("Return")
  25. }
  26. .textFieldStyle(RoundedBorderTextFieldStyle())
  27. .keyboardType(.numbersAndPunctuation)
  28. }
  29. GroupBox(label: Text("密码输入: \(pwd)")) {
  30. /// 密码输入
  31. SecureField("Password", text: $pwd)
  32. }

TextEditor

TextEditor 的使用比较简单,如下代码我们就可以轻松创建一个文本输入框:

  1. @State private var text = "Stay Hungry, Stay Foolish!"
  2. TextEditor(text: $text)
  3. .frame(height: 150)
  4. .lineSpacing(10.0) // 行距
  5. .multilineTextAlignment(.center) // 对齐方式
  6. .overlay(
  7. RoundedRectangle(cornerRadius: 5)
  8. .stroke(Color.blue, lineWidth: 1)
  9. )

系统并没有提供 placeholder 这样的特性,不过我们可以轻松实现这个功能。

  1. // 添加 placeholder
  2. ZStack(alignment: .topLeading) {
  3. TextEditor(text: $text2)
  4. .frame(height: 150)
  5. .border(Color.blue)
  6. if text2.isEmpty {
  7. Text("Type something")
  8. .foregroundColor(Color(UIColor.placeholderText))
  9. .padding(8)
  10. }
  11. }

Image

Image 用来展示图片,它可以加载资源包中的图片文件和系统内置的图标(SF Symbols)。

以下是几点提示:

  • 图片默认具有伸展特性,.resizable() 可使图片不超出屏幕或指定区域
  • 如果要展示 SF Symbols 内置图标自带的颜色,可以用 .renderingMode(.original). 来渲染。
  • Text 可以直接插入图片,支持字符串插值和拼接两种方式

本文为 Eul 样章,如果您喜欢,请移步 AppStore/Eul 查看更多内容。

原文链接:http://www.cnblogs.com/bruce2077/p/14655100.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号