我是 Swift 新手,我在我的项目中使用 SwiftUI,我下载了一些天气数据并将其显示在 ContentView() 中。
我想突出显示文本的某些部分(如果它包含某些特定单词),但我不知道如何开始。
在 ContentView() 中,我尝试设置一个函数接收从网络下载的字符串并返回一个字符串。我认为这是错误的,因为 SwiftUI 根本不会对文本应用修饰符。
例如,在我的 ContentView() 中,我希望单词雷暴具有
.bold()
修饰符:
struct ContentView: View {
let testo : String = "There is a thunderstorm in the area"
var body: some View {
Text(highlight(str: testo))
}
func highlight(str: String) -> String {
let textToSearch = "thunderstorm"
var result = ""
if str.contains(textToSearch) {
let index = str.startIndex
result = String( str[index])
}
return result
}
}
如果只需要简单的文字样式,那么这里是可能的解决方案。
使用 Xcode 11.4 / iOS 13.4 进行测试
struct ContentView: View {
let testo : String = "There is a thunderstorm in the area. Added some testing long text to demo that wrapping works correctly!"
var body: some View {
hilightedText(str: testo, searched: "thunderstorm")
.multilineTextAlignment(.leading)
}
func hilightedText(str: String, searched: String) -> Text {
guard !str.isEmpty && !searched.isEmpty else { return Text(str) }
var result: Text!
let parts = str.components(separatedBy: searched)
for i in parts.indices {
result = (result == nil ? Text(parts[i]) : result + Text(parts[i]))
if i != parts.count - 1 {
result = result + Text(searched).bold()
}
}
return result ?? Text(str)
}
}
注意:下面是以前使用过的函数,但正如@Lkabo所评论的,它对很长的字符串有限制
func hilightedText(str: String) -> Text {
let textToSearch = "thunderstorm"
var result: Text!
for word in str.split(separator: " ") {
var text = Text(word)
if word == textToSearch {
text = text.bold()
}
result = (result == nil ? text : result + Text(" ") + text)
}
return result ?? Text(str)
}
如果您的目标平台是 iOS15 / macOS12 及更高版本,则可以使用 AttributedString。例如:
private struct HighlightedText: View {
let text: String
let highlighted: String
var body: some View {
Text(attributedString)
}
private var attributedString: AttributedString {
var attributedString = AttributedString(text)
if let range = attributedString.range(of: highlighted)) {
attributedString[range].backgroundColor = .yellow
}
return attributedString
}
}
如果您希望匹配不区分大小写,您可以替换该行
if let range = attributedString.range(of: highlighted)
与
if let range = AttributedString(text.lowercased()).range(of: highlighted.lowercased())
// or
if let range = attributedString.range(of: highlighted, options: .caseInsensitive)
iOS 13、Swift 5。这篇中等文章中描述了一个通用解决方案。使用它,您可以在任何地方突出显示任何文本,唯一的问题是它的长度不能超过 64 个字符,因为它使用按位掩码。
https://medium.com/@marklucking/an-interesting-challenge-with-swiftui-9ebb26e77376
这是文章中的基本代码。
ForEach((0 ..< letter.count), id: \.self) { column in
Text(letter[column])
.foregroundColor(colorCode(gate: Int(self.gate), no: column) ? Color.black: Color.red)
.font(Fonts.futuraCondensedMedium(size: fontSize))
}
还有这个遮盖文本的...
func colorCode(gate:Int, no:Int) -> Bool {
let bgr = String(gate, radix:2).pad(with: "0", toLength: 16)
let bcr = String(no, radix:2).pad(with: "0", toLength: 16)
let binaryColumn = 1 << no - 1
let value = UInt64(gate) & UInt64(binaryColumn)
let vr = String(value, radix:2).pad(with: "0", toLength: 16)
print("bg ",bgr," bc ",bcr,vr)
return value > 0 ? true:false
}
您可以连接多个文本视图。
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View{
let testo : String = "There is a thunderstorm in the area"
let stringArray = testo.components(separatedBy: " ")
let stringToTextView = stringArray.reduce(Text(""), {
if $1 == "thunderstorm" {
return $0 + Text($1).bold() + Text(" ")
} else {
return $0 + Text($1) + Text(" ")
}
})
return stringToTextView
}
}
PlaygroundPage.current.setLiveView(ContentView())
@Asperi 的答案效果很好。这是一个修改后的变体,通过单个单词数组进行搜索:
func highlightedText(str: String, searched: [String]) -> Text {
guard !str.isEmpty && !searched.isEmpty else { return Text(str) }
var result: Text!
let parts = str.components(separatedBy: " ")
for part_index in parts.indices {
result = (result == nil ? Text("") : result + Text(" "))
if searched.contains(parts[part_index].trimmingCharacters(in: .punctuationCharacters)) {
result = result + Text(parts[part_index])
.bold()
.foregroundColor(.red)
}
else {
result = result + Text(parts[part_index])
}
}
return result ?? Text(str)
}
使用示例:
let str: String = "There is a thunderstorm in the area. Added some testing long text to demo that wrapping works correctly!"
let searched: [String] = ["thunderstorm", "wrapping"]
highlightedText(str: str, searched: searched)
.padding()
.background(.yellow)
你也可以用markdown这样制作AttributedString
do {
return try AttributedString(markdown: foreignSentence.replacing(word.foreign, with: "**\(word.foreign)**"))
} catch {
return AttributedString(foreignSentence)
}
只需使用文本
Text(foreignSentenceMarkdown)
感谢@Asperi 和@Maria N. 提供的有用代码。效果很好。
我已对其进行了调整,使其无需空格分隔的输入即可工作。这是修改后的版本:
func highlightedText(str: String, searched: [String]) -> Text {
guard !str.isEmpty && !searched.isEmpty else { return Text(str) }
var str = str
let separatorText = "&%@$"
for search in searched {
str = str.replacingOccurrences(of: search, with: separatorText + search + separatorText)
}
var result: Text!
let parts = str.components(separatedBy: separatorText)
for part_index in parts.indices {
result = (result == nil ? Text("") : result + Text(" "))
if searched.contains(parts[part_index].trimmingCharacters(in: .punctuationCharacters)) {
result = result + Text(parts[part_index])
.bold()
.foregroundColor(.red)
} else {
result = result + Text(parts[part_index])
}
}
return result ?? Text(str)
}
使用示例:
let str: String = "1hr 20min"
let searched: [String] = ["hr", "min"]
highlightedText(str: str, searched: searched)