2

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)
            }
    }
}
Ken White
  • 123,280
  • 14
  • 225
  • 444
Stanley
  • 21
  • 3

0 Answers0