如何在文本视图中突出显示选定的文本?

huangapple go评论69阅读模式
英文:

How can I highlight selected text in a Text view?

问题

I display a text in different colors and the user needs to be able to copy a portion of said text. I want them to be able to highlight the text they want to copy (like in a pdf or in a messenger/email).

Since I need different colors I used multiple Text views in a VStack:

var body: some View {
    VStack(alignment: .leading, spacing: 0) {
        Spacer()
        ForEach(logMessages, id: \.id) { message in
            messageText(message)
        }
        Spacer()
    }
    .edgesIgnoringSafeArea(.all)
    .padding()
}

I want the .background part to be my highlighting for the selected text.

I couldn't find any information on how to tackle this specific problem.

英文:

I display a text in different colors and the user needs to be able to copy a portion of said text. I want them to be able to highlight the text they want to copy (like in a pdf or in a messenger/email).

Since I need different colors I used multiple Text views in a VStack:

    var body: some View {
    VStack(alignment: .leading, spacing: 0) {
        Spacer()
        ForEach(logMessages, id: \.id) { message in
            messageText(message)
        }
        Spacer()
    }
    .edgesIgnoringSafeArea(.all)
    .padding()
}

    private func messageText(_ message: LogMessage) -> some View {
    return
        Text("\(message.levelString): \(message.message)")
            .font(.system(size: fontSize))
            .frame(maxWidth: .infinity, alignment: .leading)
            .foregroundColor(logColor(message.level.rawValue))
            .textSelection(.enabled)
    //                .background( // meant to be for highlighting selected text
    //                    RoundedRectangle(cornerRadius: 4)
    //                        .foregroundColor(.blue)
    //                        .opacity(0.3)
    //                )
        }

I want the .background part to be my highlighting for the selected text.

I couldn't find any information on how to tackle this specific problem.

答案1

得分: 1

在SwiftUI中,Text视图不原生支持文本选择功能。但是,您可以创建一个自定义的TextSelectable视图,通过使用UIKit代码,通过UIViewRepresentable将其集成到SwiftUI中,从而启用文本选择功能。我已经附上了整个测试了选择功能的示例代码。

import SwiftUI

struct TextSelectionView: View {
    let logMessages: [LogMessage] = [
        LogMessage(levelString: "Info", message: "This is an information message.", level: 1),
        LogMessage(levelString: "Warning", message: "This is a warning message.", level: 2),
        LogMessage(levelString: "Error", message: "This is an error message.", level: 3)
    ]
    
    @State private var selectedRange: NSRange?
    
    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            Spacer()
            ForEach(logMessages, id: \.id) { message in
                messageText(message, selectedRange: $selectedRange)
            }
            Spacer()
        }
        .edgesIgnoringSafeArea(.all)
        .padding()
    }
    
    private func messageText(_ message: LogMessage, selectedRange: Binding<NSRange?>) -> some View {
        var selectedRange: NSRange?
        
        let text = TextSelectable(text: "\(message.levelString): \(message.message)", selectedRange: selectedRange) { range in
            selectedRange = range
        }
            .font(.system(size: 14))
            .frame(maxWidth: .infinity, alignment: .leading)
            .background(
                RoundedRectangle(cornerRadius: 4)
                    .foregroundColor(selectedRange != nil ? .blue.opacity(0.3) : .clear)
            )
        return text
    }
    
}

struct TextSelectionView_Previews: PreviewProvider {
    static var previews: some View {
        TextSelectionView()
    }
}

struct TextSelectable: UIViewRepresentable {
    let text: String
    var selectedRange: NSRange?
    var onSelectedRangeChanged: ((NSRange?) -> Void)?
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.isSelectable = true
        textView.isUserInteractionEnabled = true
        textView.font = UIFont.systemFont(ofSize: 16)
        textView.delegate = context.coordinator
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.text = text
        
        if uiView.selectedRange != selectedRange {
            uiView.selectedRange = selectedRange ?? NSRange(location: 0, length: 0)
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextViewDelegate {
        let parent: TextSelectable
        
        init(_ parent: TextSelectable) {
            self.parent = parent
        }
        
        func textViewDidChangeSelection(_ textView: UITextView) {
            parent.onSelectedRangeChanged?(textView.selectedRange)
        }
    }
}

struct LogMessage: Identifiable {
    let id = UUID()
    let levelString: String
    let message: String
    let level: Int
}

在这里,我们将NSRange状态变量传递给TextSelectable绑定,该绑定处理文本的选择。如果您有任何问题,请随时提问。

英文:

The Text view in SwiftUI does not natively support text selection. However, you can create a custom TextSelectable view to enable text selection functionality by the help of uikit code that you can merge in swiftui using UIViewRepresentable. I have attached the whole where I tested the selection functionality.

import SwiftUI
struct TextSelectionView: View {
let logMessages: [LogMessage] = [
LogMessage(levelString: &quot;Info&quot;, message: &quot;This is an information message.&quot;, level: 1),
LogMessage(levelString: &quot;Warning&quot;, message: &quot;This is a warning message.&quot;, level: 2),
LogMessage(levelString: &quot;Error&quot;, message: &quot;This is an error message.&quot;, level: 3)
]
@State private var selectedRange: NSRange?
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Spacer()
ForEach(logMessages, id: \.id) { message in
messageText(message, selectedRange: $selectedRange)
}
Spacer()
}
.edgesIgnoringSafeArea(.all)
.padding()
}
private func messageText(_ message: LogMessage, selectedRange: Binding&lt;NSRange?&gt;) -&gt; some View {
var selectedRange: NSRange?
let text = TextSelectable(text: &quot;\(message.levelString): \(message.message)&quot;, selectedRange: selectedRange) { range in
selectedRange = range
}
.font(.system(size: 14))
.frame(maxWidth: .infinity, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 4)
.foregroundColor(selectedRange != nil ? .blue.opacity(0.3) : .clear)
)
return text
}
}
struct TextSelectionView_Previews: PreviewProvider {
static var previews: some View {
TextSelectionView()
}
}
struct TextSelectable: UIViewRepresentable {
let text: String
var selectedRange: NSRange?
var onSelectedRangeChanged: ((NSRange?) -&gt; Void)?
func makeUIView(context: Context) -&gt; UITextView {
let textView = UITextView()
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.font = UIFont.systemFont(ofSize: 16)
textView.delegate = context.coordinator
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
if uiView.selectedRange != selectedRange {
uiView.selectedRange = selectedRange ?? NSRange(location: 0, length: 0)
}
}
func makeCoordinator() -&gt; Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UITextViewDelegate {
let parent: TextSelectable
init(_ parent: TextSelectable) {
self.parent = parent
}
func textViewDidChangeSelection(_ textView: UITextView) {
parent.onSelectedRangeChanged?(textView.selectedRange)
}
}
}
struct LogMessage: Identifiable {
let id = UUID()
let levelString: String
let message: String
let level: Int
}

Here we passing NSRange state variable to TextSelectable binding which handle the selection of text. Feel free to ask question if you have.

huangapple
  • 本文由 发表于 2023年7月6日 17:18:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76627312.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定