在SwiftUI中使文本的某些部分加粗

31
我在想如何在SwiftUI中使文本的某些部分变为粗体,而保持其他部分“常规”呢?
我目前有:
Text("Coronavirus Disease of 2019")

我希望它能够打印出2019年的COronaVirus Disease,并且一直没能成功地只让某些部分加粗。

8个回答

63

iOS 15+(Swift 5.5 +)

SwiftUI内置支持呈现Markdown。

它是GitHub风格的Markdown。 AttributedString会转换内联样式和块样式。SwiftUI呈现内联样式(但目前不包括图像)。我们使用了出色的cmark-gfm库来解析Markdown字符串。-SwiftUI Frameworks Engineer - developer.apple.com

更多信息:

什么是Markdown?


使用双星号(**)将要加粗的字符括起来。

Text("**CO**rona**V**irus **D**isease of 20**19**")

使用下划线 (_)将要斜体化的字符括起来。

Text("Is this text _emphasized_?")

字符串变量

使用init(_ value: String)

从给定的字符串值创建本地化字符串键。

let bold = "This text is **bold**"
Text(.init(bold))

字符串插值

使用init(_ value: String)方法。

从给定的字符串值创建本地化字符串键。

let bold = "Bold"
Text(.init("This text is **\(bold)**"))

属性字符串

使用 init(_ attributedContent: AttributedString)

创建一个显示已设置的属性内容的文本视图。

let markdownText = try! AttributedString(markdown: "This text is **bold**")
Text(markdownText)

另请参阅:

init(_ attributedContent: AttributedString) - https://developer.apple.com


实际上,**Bold**__Bold__都可以产生粗体效果(而*Italic*_Italic_则会呈现斜体)。 - JKvr
需要补充说明的是,如果字符串由于大小而被分成几个部分,例如:Text("Hello there " + "world"),则文本将在不加粗的情况下呈现。 - barryalan2633
2
@barryalan2633 更新了我的答案。你需要使用 Text(.init("Hello there " + "world"))。 - mahan

27
如果您不需要翻译,这里可能有快速的变体。

demo

Text("CO").bold() + Text("rona") + Text("VI").bold() + 
    Text("rus Disease of 20") + Text("19").bold()

另一种方法是使用NSAttributedString和UIViewRepresentableUILabel


15

简单说明一下,如果你需要将框架或填充修饰符应用于文本,则需要先对文本进行分组,然后再向组添加修饰符。

Group { Text("CO").bold() + Text("rona") + Text("VI").bold() + Text("rus Disease of 20") + Text("19").bold() }.frame(width: 100, height: 100).padding(.horizontal)

6

这类问题经常出现,对于没有Objective-C背景的Swift新手来说,解决方案很难得出。上面的一些答案是优秀的,但总结起来,也许作为问题的最佳解决方案的是:

Group {
    Text("CO").bold() +
    Text("rona") +
    Text("V").bold() +
    Text("irus ") +
    Text("D").bold() +
    Text("isease of 20") +
    Text("19").bold()
}
.font(.caption)
.frame(width: 300)

(Group{}对我来说是个秘密武器)


2

iOS 14+

@mahan提出的解决方案很棒,但它有一个限制,即在iOS 15上运行良好,但在iOS 14上不行。

因此,我认为这是一个更好的解决方案,适用于那些需要支持iOS 14的人。该解决方案是从这个网站复制过来的: https://www.avanderlee.com/swiftui/text-weight-combinations/

最终代码如下所示:

    @main
struct RichTextApp: App {
    var body: some Scene {
        WindowGroup {
            RichText("SwiftLee - A *weekly blog* about Swift, iOS and Xcode *Tips and Tricks*")
                .padding()
                .multilineTextAlignment(.center)
        }
    }
}

(您可以自定义字体并在文本中使用变量,例如:)
RichText(" ... *\(viewModel.title)* ...")

enter image description here

而代码是:

import SwiftUI

struct RichText: View {

    struct Element: Identifiable {
        let id = UUID()
        let content: String
        let isBold: Bool

        init(content: String, isBold: Bool) {
            var content = content.trimmingCharacters(in: .whitespacesAndNewlines)

            if isBold {
                content = content.replacingOccurrences(of: "*", with: "")
            }

            self.content = content
            self.isBold = isBold
        }
    }

    let elements: [Element]

    init(_ content: String) {
        elements = content.parseRichTextElements()
    }

    var body: some View {
        var content = text(for: elements.first!)
        elements.dropFirst().forEach { (element) in
            content = content + self.text(for: element)
        }
        return content
    }
    
    private func text(for element: Element) -> Text {
        let postfix = shouldAddSpace(for: element) ? " " : ""
        if element.isBold {
            return Text(element.content + postfix)
                .fontWeight(.bold)
        } else {
            return Text(element.content + postfix)
        }
    }

    private func shouldAddSpace(for element: Element) -> Bool {
        return element.id != elements.last?.id
    }
    
}



extension String {

    /// Parses the input text and returns a collection of rich text elements.
    /// Currently supports asterisks only. E.g. "Save *everything* that *inspires* your ideas".
    ///
    /// - Returns: A collection of rich text elements.
    func parseRichTextElements() -> [RichText.Element] {
        let regex = try! NSRegularExpression(pattern: "\\*{1}(.*?)\\*{1}")
        let range = NSRange(location: 0, length: count)

        /// Find all the ranges that match the regex *CONTENT*.
        let matches: [NSTextCheckingResult] = regex.matches(in: self, options: [], range: range)
        let matchingRanges = matches.compactMap { Range<Int>($0.range) }

        var elements: [RichText.Element] = []

        // Add the first range which might be the complete content if no match was found.
        // This is the range up until the lowerbound of the first match.
        let firstRange = 0..<(matchingRanges.count == 0 ? count : matchingRanges[0].lowerBound)

        self[firstRange].components(separatedBy: " ").forEach { (word) in
            guard !word.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
            elements.append(RichText.Element(content: String(word), isBold: false))
        }

        // Create elements for the remaining words and ranges.
        for (index, matchingRange) in matchingRanges.enumerated() {
            let isLast = matchingRange == matchingRanges.last

            // Add an element for the matching range which should be bold.
            let matchContent = self[matchingRange]
            elements.append(RichText.Element(content: matchContent, isBold: true))

            // Add an element for the text in-between the current match and the next match.
            let endLocation = isLast ? count : matchingRanges[index + 1].lowerBound
            let range = matchingRange.upperBound..<endLocation
            self[range].components(separatedBy: " ").forEach { (word) in
                guard !word.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
                elements.append(RichText.Element(content: String(word), isBold: false))
            }
        }

        return elements
    }

    /// - Returns: A string subscript based on the given range.
    subscript(range: Range<Int>) -> String {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        let endIndex = index(self.startIndex, offsetBy: range.upperBound)
        return String(self[startIndex..<endIndex])
    }
}

这很好,但似乎会从文本中删除新行? - dqualias
@dqualias,\n 对您不起作用吗? 例如 "(标题) \n (内容)" - Tiago Mendes
没错,如果我有一个字符串像“标题 \n内容”,那么换行符就会被删除。我想正则表达式正在删除换行符? - dqualias
行"var content = content.trimmingCharacters(in: .whitespacesAndNewlines)"移除换行符。所以可能改为"var content = content.trimmingCharacters(in: .whitespaces)"。 - Claytog

1

Swift 5,iOS 13

本文介绍如何更改字符文本的颜色,但您同样可以使用该技术(位掩码)来使一些字符加粗、闪烁、动画等。

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
}

0

不仅可以使用加粗...

您可以使用AttributedString将任何属性(加粗、颜色、字体等)应用于字符串的任何部分:

iOS 15

Text现在支持Markdown,您还可以创建自定义属性:

enter image description here

你甚至可以远程获取已定义的属性,例如:

enter image description here

支持所有iOS版本的完全回退!

由于直到iOS 15都不支持Text,所以您可以将UILabel带到那里并根据需要进行修改:

实现:
struct UIKLabel: UIViewRepresentable {

    typealias TheUIView = UILabel
    fileprivate var configuration = { (view: TheUIView) in }

    func makeUIView(context: UIViewRepresentableContext<Self>) -> TheUIView { TheUIView() }
    func updateUIView(_ uiView: TheUIView, context: UIViewRepresentableContext<Self>) {
        configuration(uiView)
    }
}
使用方法:
var body: some View {
    UIKLabel {
        $0.attributedText = NSAttributedString(string: "HelloWorld")
    }
}

0
通过使用AttributedString,您可以使用以下扩展功能。

extension View {
    func attributedBold(text: String, boldText: String,
                        font: Font = .largeTitle) -> AttributedString
    {
        var result = AttributedString(text)
        result.font = font
        if let range = result.range(of: boldText) {
            result[range].font = font.bold()
        }
        result.foregroundColor = .white
        return result
    }
}

只需调用

 Text(attributedBold(text: "Make me bold with attributed string",
                                        boldText: "bold",
                                        font: .title2))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接