I am trying to create a note-taking app, but I can't figure out how to let the user format the text. Something like Apple Notes app, where I can select some text, bold/underline/strikethrough/italic the selected text only
I tried using UITextView as the text editor, and use .selectedRange to detect which part of the text is selected. I converted the string into NSMutableAttributedString to be displayed on a UILabel. But I can't addAttributes to the NSMutableAttributedString (except when initialising the UILabel)
UITextView:
struct UITextViewRepresentable: UIViewRepresentable {
let textView = UITextView()
@Binding var text: String
@Binding var selectedRange: NSRange
func makeUIView(context: Context) -> UITextView {
textView.delegate = context.coordinator
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
// SwiftUI -> UIKit
uiView.text = text
}
func makeCoordinator() -> Coordinator {
Coordinator(text: $text, selectedRange: $selectedRange)
}
class Coordinator: NSObject, UITextViewDelegate {
@Binding var text: String
@Binding var selectedRange: NSRange
init(text: Binding<String>, selectedRange: Binding<NSRange>) {
self._text = text
self._selectedRange = selectedRange
}
func textViewDidChange(_ textView: UITextView) {
// UIKit -> SwiftUI
_text.wrappedValue = textView.text
}
func textViewDidChangeSelection(_ textView: UITextView) {
// Fires off every time the user changes the selection.
selectedRange = textView.selectedRange
}
}
}
UILabel:
struct AttributedText: UIViewRepresentable {
let attributedString: NSMutableAttributedString
init(_ attributedString: NSMutableAttributedString) {
self.attributedString = attributedString
attributedString.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 7, length: 5))
}
func makeUIView(context: Context) -> UILabel {
let label = UILabel()
label.lineBreakMode = .byClipping
label.numberOfLines = 0
return label
}
func updateUIView(_ uiView: UILabel, context: Context) {
uiView.attributedText = attributedString
}
}
ContentView:
struct ContentView: View {
@State private var text = "placeholder text in UITextView"
var attributedText: NSMutableAttributedString {
return NSMutableAttributedString(string: self.text)
}
@State private var selectedRange = NSRange(location: 0, length: 0)
var body: some View {
VStack {
UITextViewRepresentable(text: $text, selectedRange: $selectedRange)
AttributedText(attributedText)
}
}
}