英文:
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: "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
}
Here we passing NSRange state variable to TextSelectable binding which handle the selection of text. Feel free to ask question if you have.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论