Text 文本

// 基本调用方法:Text 后面跟的参数必须是 String 类型
Text("Stay Hungry. Stay Foolish.")

// 字符串插值:如果要展示的信息不是 String 类型,需要用 \\() 进行插值
Text("\\(price)")

使用第三方字体

<aside> 💡 关于 iOS 设备默认的可用字体可以看这: http://iosfonts.com/ 。如果没有合适的字体,你还可以使用自己下载的合法的第三方字体。

</aside>

  1. 首先下载字体文件,并拖入项目文件夹里。

    将字体文件拖到 Xcode 的项目导航器中。可以一次拖动包含 TrueType (.ttf) 或 OpenType (.otf) 文件的整个文件夹。接受默认设置;您确实想要复制项目、创建组并添加到当前目标。

    您可以通过在项目导航器中选择字体文件,然后使用 Command-Option-1 打开 Xcode 的文件检查器窗格来检查字体文件是否已正确添加到当前目标。在项目导航器中突出显示每个字体文件,并确保其目标成员资格显示您应用程序的目标。

  2. 制作修改器 ViewModifier (其中字体名不需要加后缀)

    struct CustomFontModifier : ViewModifier {
        var size : CGFloat = 28
        func body(content: Content) -> some View {
            content
                .font(.custom("Oswald-Regular", size: size))
        }
    }
    
  3. 修改 Info.plist 属性列表,新增 【Fonts provided by application】 字段

    点击最左边导航中的项目根图标,找到Targets,点击项目名,在右边找到 info 子菜单。在Custom iOS Target Properties中,新增 Fonts provided by application 。做完后,左侧的项目导航目录里会多一项 info 文件。在 Fonts provided by application 里新增 item,后面填上字体的名称即可,有几个字体就新增几项。

  4. 在字段里逐个填入 item,即字体文件名,如:Oswald-Bold.ttf

使用 + 号连接文本

//可以用 + 号连接多个文本(默认基线会对齐)
Text("Here is another ")
+ Text("example").foregroundColor(.red).underline()
+ Text("Notice").foregroundColor(.purple).bold()
+ Text("as a whole.").bold().italic()

//可以用 + 号连接多个文本(设置基线偏移,解决基线不对齐问题,负数代表往下)
Text("100").bold()
	+ Text(" SWIFTUI ")
		.baselineOffset(-12)
	+ Text ("VIEWS").bold()

使用 redacted 显示占位符

// 1. 将文本显示为占位符
// SwiftUI 允许将文本标记为视图中的占位符,这意味着它会被渲染,但会被灰色遮盖以表明它不是最终内容。
// 这是通过 redacted(reason:) 修饰符以及可用于根据需要覆盖密文的 unredacted() 修饰符提供的。
Text("This is placeholder text").redacted(reason: .placeholder)

// 还可以加到父元素上
VStack(alignment: .leading) {
    Text("This is placeholder text This is placeholder telder tex")
    Text("And so is this")
}.redacted(reason: .placeholder)

// 2. 还可以查询从环境传入的任何编辑原因
struct ContentView: View {
    @Environment(\\.redactionReasons) var redactionReasons
    let bio = "The rain in Spain falls mainly on the Spaniards"
    var body: some View {
        if redactionReasons == .placeholder {
            Text("Loading…")
        } else {
            Text(bio)
                .redacted(reason: redactionReasons)
        }
    }
}

// 3. 还允许将视图的某些部分标记为包含敏感信息,这使我们可以使用密文更轻松地隐藏或显示它
// 要使用此功能请将 privacySensitive() 修饰符添加到应隐藏的任何视图
// 然后在更高的视图层次结构中应用 .redacted(reason: .privacy) 修饰符
VStack {
    Text("Card number")
        .font(.headline)

    if redactionReasons.contains(.privacy) {
        Text("[HIDDEN]").privacySensitive()
    } else {
        Text("1234 5678 9012 3456").privacySensitive()
    }
}
.redacted(reason: .privacy)

// 4.默认情况下,隐私敏感上下文被灰色框屏蔽,但您也可以通过从环境中读取密文原因来提供自定义布局:
struct ContentView: View {
    @Environment(\\.redactionReasons) var redactionReasons

    var body: some View {
        VStack {
            Text("Card number")
                .font(.headline)

            if redactionReasons.contains(.privacy) {
                Text("[HIDDEN]")
            } else {
                Text("1234 5678 9012 3456")
            }
        }
    }
}

呈现 Markdown 内容

Text 视图提供了两种使用 Markdown 设置文本样式的方法:直接在 Text 视图中和使用 AttributedString 。

直接在 Text 视图中使用 Markdown 对于静态文本来说很方便,但对于动态字符串或当您想要将样式应用于字符串的不同部分时,您需要使用 AttributedString 以编程方式。

// SwiftUI 内置了对 Markdown 渲染的支持,包括粗体、斜体、链接等
// 它实际上内置于 SwiftUI 的 Text 视图中,因此您可以编写如下代码:
VStack {
    Text("This is regular text.")
    Text("* This is **bold** text, this is *italic* text, and this is ***bold, italic*** text.")
    Text("~~A strikethrough example~~")
    Text("`Monospaced works too`")
    Text("Visit Apple: [click here](<https://apple.com>)")

    // 该链接是可自动点击的。默认情况下,Markdown 链接将使用应用程序的强调色,但可以使用 tint() 修饰符更改它:
    Text("Visit Apple: [click here](<https://apple.com>)")
        .tint(.indigo)

    // 注意:不支持图像

}

// 支持自动 Markdown 转换是因为 SwiftUI 将这些字符串解释为 LocalizedStringKey 实例(即可以由应用程序本地化的字符串)
// 这意味着如果您想从属性或变量创建 Markdown 文本,您应该将其显式标记为 LocalizedStringKey 以获得 Markdown 渲染:

struct ContentView: View {
    let markdownText: LocalizedStringKey = "* This is **bold** text, this is *italic* text, and this is ***bold, italic*** text."
    var body: some View {
        Text(markdownText)
    }
}

// 如果希望原始文本保持不变(即将原始的、未格式化的 Markdown 符号保留在原处),只需删除 LocalizedStringKey 注释即可
// 或者,您可以使用 Text(verbatim:) 初始值设定项完全禁用 Markdown 和本地化。

https://www.hackingwithswift.com/img/books/quick-start/swiftui/how-to-format-text-inside-text-views-4.png


AttributedString 高级字符串

Text 视图能够渲染 Foundation 的 AttributedString 结构,从而实现添加下划线、删除线、网页链接、背景颜色等效果。对于更高级或动态的样式, AttributedString 是首选工具。它允许您将不同的样式应用于字符串的不同部分。

创建方法

AttributedString 的创建方式,是先声明一个 AttributedString 对象,然后再用点语法设置各个属性。

struct ContentView: View {

		// 声明:先声明一个 AttributedString 对象,再设置其各种属性
		var message: AttributedString {
				var result = AttributedString("Hello, world!")
        result.font = .largeTitle
				return result
    }

		// 使用时:作为字符串传入到 Text
		var body:some View {
        Text(message)
    }
    
}

常见属性

遗憾的是, AttributedString 的 API 不大透明 ,所以这里展示一大堆示例来帮助入门。

// 字体大小
result.font = .largeTitle

// 前景色
result.foregroundColor = .white

// 背景色
result.backgroundColor = .red

// 下划线图案和颜色
result.underlineStyle = Text.LineStyle(pattern: .solid, color: .white)

// 使用 link 属性将可点击的网络链接附加到文本中
result.link = URL(string: "<https://www.hackingwithswift.com>")

// 还可以调整字符串各部分的基线偏移,强制将其放置在高于或低于默认值的位置:
struct ContentView: View {

		var message: AttributedString {
				let string = "The letters go up and down"
				var result = AttributedString()
				for (index, letter) in string.enumerated() {
						var letterString = AttributedString(String(letter))
            letterString.baselineOffset = sin(Double(index)) * 5
            result += letterString
        }
        result.font = .largeTitle
				return result
    }

		var body:some View {
        Text(message)
    }
}

使用 AttributedString 让 Text 展示多个样式:

该示例仅使用 Text 和常规 SwiftUI 修饰符来完成,但 AttributedString 的部分功能是自定义属于 String 而不是 Text 。这意味着背景颜色是 String 本身的一部分,因此如果需要,我们可以使用不同的背景颜色将多个 String 合并在一起:

struct ContentView: View {

		// 声明红色背景的字符串
		var message1: AttributedString {
				var result = AttributedString("Hello")
        result.font = .largeTitle
        result.foregroundColor = .white
        result.backgroundColor = .red
				return result
    }

		// 声明蓝色背景的字符串
		var message2: AttributedString {
				var result = AttributedString("World!")
        result.font = .largeTitle
        result.foregroundColor = .white
        result.backgroundColor = .blue
				return result
    }

		// 使用时:
		// 如果您尝试对 Text 使用 background() 修饰符,会发现它不起作用。
		var body:some View {
        Text(message1 + message2)
    }
    
}

<aside> 💡 您可以将 SwiftUI 修饰符与 AttributedString 属性混合使用。重要的是,通过 AttributedString 应用的属性优先于 SwiftUI 修饰符。

</aside>

使用 MarkDown

在此示例中, AttributedString 使用 Markdown 语法来制作一些斜体文本。当此 AttributedString 传递到 Text 视图时,这些样式将被保留。当将 font 和 foregroundColor 修饰符应用于 Text 视图时,这些样式将应用于整个文本,但它们不会覆盖粗体和由 AttributedString 指定的斜体样式。

这是因为 AttributedString 的样式优先于 SwiftUI 修饰符。

let quote = """
**"Be yourself;** everyone else is _already taken._"
- **Oscar Wilde**
"""
let attributedQuote = try! AttributedString(markdown: quote)

struct ContentView: View {
  var body: some View {
    Text(attributedQuote)
      .font(.system(size: 16, weight: .medium, design: .serif))
      .foregroundColor(.blue)
  }
}

使用元数据

AttributedString 真正强大的功能是它不会丢弃我们提供的有关字符串的所有元数据,这解锁了大量额外的功能。例如,出于可访问性原因,我们可以将字符串的一部分标记为需要拼写,以便在使用 VoiceOver 时正确读出密码等内容:

struct ContentView: View {
		var message: AttributedString {
				var password = AttributedString("abCayer-muQai")
        password.accessibilitySpeechSpellsOutCharacters = true
				return "Your password is: " + password
    }

		var body:some View {
        Text(message)
    }
}

更令人印象深刻的是它处理结构化信息的方式。

例如,如果我们将 Date 实例格式化为属性字符串,它会保留每个组件代表的内容的知识,它会记住“November”是字符串的月份部分。这意味着我们可以根据语义设置字符串的样式:我们可以设置 “工作日部分具有主要颜色,而其他具有次要颜色”,如下所示:

struct ContentView: View {

		var message: AttributedString {
				var result = Date.now.formatted(.dateTime.weekday(.wide).day().month(.wide).attributed)
        result.foregroundColor = .secondary
				let weekday = AttributeContainer.dateField(.weekday)
				let weekdayStyling = AttributeContainer.foregroundColor(.primary)
        result.replaceAttributes(weekday, with: weekdayStyling)
				return result
    }

		var body:some View {
        Text(message)
    }

}

// 请注意:该代码不知道工作日实际出现在文本中的位置(它与语言和区域设置无关),因此它可以为每个人正确设置样式。

使用 PersonNameComponents 处理人名也是如此。以下例子产生一个 AttributedString 实例,其中某人的姓氏是粗体的:

struct ContentView: View {

		var message: AttributedString {
				var components = PersonNameComponents()
        components.givenName = "Taylor"
        components.familyName = "Swift"
        var result = components.formatted(.name(style: .long).attributed)
				let familyNameStyling = AttributeContainer.font(.headline)
				let familyName = AttributeContainer.personNameComponent(.familyName)
        result.replaceAttributes(familyName, with: familyNameStyling)
				return result
    }

		var body:some View {
        Text(message)
    }
    
}

您甚至可以将其用于测量。例如,以下代码创建 200 公里的测量值,然后对其进行格式化,以便显示的值远大于单位:

struct ContentView: View {
		var message: AttributedString {
				var amount = Measurement(value: 200, unit: UnitLength.kilometers)
				var result = amount.formatted(.measurement(width: .wide).attributed)
				let distanceStyling = AttributeContainer.font(.title)
				let distance = AttributeContainer.measurement(.value)
        result.replaceAttributes(distance, with: distanceStyling)
				return result
    }

		var body:some View {
        Text(message)
    }
}
// 作为奖励,这将自动尊重用户对距离的区域设置偏好,这意味着许多用户将看到“124 英里”而不是“200 公里”。

<aside> 💡 警告:如果您使用 Xcode 的自动完成功能探索 API,您将看到各种看起来应该有效的选项,但实际上都是无效的。

</aside>


Label 标签

// 方式:使用 SF 符号
Label("Welcome to the app", systemImage: "folder.circle")

// 方式:使用自己的图片资源
Label("Welcome to the app", image: "star")

// 方式:Label 包含 title 和 icon 两个属性,可以快速放置 图片 和 文字,而不必使用布局视图
Label(
	title:{ 
		Text("Delete").fontWeight(.semibold)
	}
	icon:{
		Image(systemName:"trash").font(.title)
	}
)

// 方式:还可以为文本和图像提供完全自定义的视图
Label {
    Text("Paul Hudson")
        .foregroundStyle(.primary)
        .font(.largeTitle)
        .padding()
        .background(.gray.opacity(0.2))
        .clipShape(Capsule())
} icon: {
    RoundedRectangle(cornerRadius: 10)
        .fill(.blue)
        .frame(width: 64, height: 64)
}

**控制标签显示方式:**通过 .labelStyle() 修饰符,可以控制标签的显示方式,其支持参数值有automatic.titleOnly 、 .iconOnly  和  .titleAndIcon

VStack {
    Label("Text Only", systemImage: "heart")
        .font(.title)
        .labelStyle(.titleOnly)

    Label("Icon Only", systemImage: "star")
        .font(.title)
        .labelStyle(.iconOnly)

    Label("Both", systemImage: "paperplane")
        .font(.title)
        .labelStyle(.titleAndIcon)
}

format 设置文本格式

SwiftUI 的文本视图能够通过设置 format 参数,从而显示日期、数组、测量值等。不过,此功能仅在 iOS 15 中可用,因此对于 iOS 14 和 13 支持,请参阅下面的 formatter 参数。

显示数字

// 显示为数字
Text("转换结果是: \\(finalValue.formatted(.number))")

// 保留小数点后两位
Text("转换结果是: \\(String(format: "%.2f", finalValue))")

.list 连接数组

将 .list() 格式类型与字符串数组一起使用,则可以获得格式整齐的列表展示,例如“Howard、Josie 和 Kingsley”。

// 例如,无论添加多少项目,这都将正确打印成员列表:
struct ContentView: View {
    @State private var ingredients = [String]()
		var body:some View {
        VStack {
            Text(ingredients, format: .list(type: .and))
            Button("Add Ingredient") {
								let possibles = ["Egg", "Sausage", "Bacon", "Spam"]
								if let newIngredient = possibles.randomElement() {
                    ingredients.append(newIngredient)
                }
            }
        }
    }
}
// 显示: Spam, Egg, Sausage, and Bacon

如果是数字类型的数组(例如整数),则可以通过指定 memberStyle 来格式化该数组,如下所示:

struct ContentView: View {
    @State private var rolls = [Int]()
		var body:some View {
        VStack {
            Text(rolls, format: .list(memberStyle: .number, type: .and))
            Button("Roll Dice") {
								let result = Int.random(in: 1...6)
                rolls.append(result)
            }
        }
    }
}
// 显示: 4, 3, 2, and 6

.measurement 显示测量值

当您正在处理距离或重量等测量值, .measurement() 格式类型将自动调整您的值以在用户区域设置中显示。例如,如果您在内部存储以厘米为单位的值,但用户设备上的区域设置为美国,iOS 将根据值的大小自动显示以英尺或英寸为单位的值。

struct ContentView: View {
		let length = Measurement(value: 225, unit: UnitLength.centimeters)
		var body:some View {
        Text(length, format: .measurement(width: .wide))
    }
}

.currency 显示货币价格

还有一个格式化程序可以正确处理货币,确保显示两位小数,并根据需要添加货币符号:

Text(72.3, format: .currency(code: "CAD"))

//使用当地语言的货币符号(用户如果没有设置,则使用默认值 USD)
TextField(
    "Amount", value: $checkAmount, format: .currency(code: Locale.current.currency?.identifier ?? "USD")
)

Date 显示日期格式

SwiftUI 的文本视图附带两种特定的日期格式化程序,以使日期在屏幕上看起来更好:一种用于处理单个日期,另一种用于处理日期范围。

**日期范围:**实际上更简单,因为您只需提供一个封闭的日期范围,它将确保其根据用户的区域设置进行适当的格式化: