Search code examples
swiftswiftuisyntaxsyntax-highlightingnsattributedstring

SwiftUI value not getting updated


I have a SwiftUI Texteditor and whenever I press the update button the color should be applied to the text however it just gets reseted to initialValue. Can anyone help me?

import SwiftUI

class TextColorUpdater: ObservableObject {

    @Published var attributedText: NSAttributedString

    init(initialText: String) {
        self.attributedText = TextColorUpdater.highlightText(initialText)
    }
    
    func update() {
        print(attributedText.string)
        self.attributedText = TextColorUpdater.highlightText(attributedText.string)
    }
    
    static func highlightText(_ text: String) -> NSAttributedString {
        let mutableAttributedString = NSMutableAttributedString(string: text)
        
        if let regex = try? NSRegularExpression(pattern: "\\bred\\b", options: .caseInsensitive) {
            let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count))
            for match in matches {
                let nsRange = match.range
                mutableAttributedString.addAttribute(.foregroundColor, value: UIColor.red, range: nsRange)
            }
        }
        
        return mutableAttributedString
    }

}

struct HighlightedTextEditor: UIViewRepresentable {
    
    @Binding var attributedText: NSAttributedString

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.isEditable = true
        textView.isSelectable = true
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.attributedText = attributedText
    }

}

struct ContentView: View {

    @StateObject var textColorUpdater = TextColorUpdater(initialText: "This is a redd red red red car. The sky is not red")

    var body: some View {
        VStack {
            HighlightedTextEditor(attributedText: $textColorUpdater.attributedText)
            
            Button("Update") {
                textColorUpdater.update()
            }
        }
    }

}




Solution

  • When the text of UITextView changes, you never update the @Binding! The binding is not going to know that it is related to the UITextView's text and update itself!

    You need to detect the text change of UITextView. You can use UITextViewDelegate to do that. For conforming to that, you need a Coordinator.

    struct HighlightedTextEditor: UIViewRepresentable {
        
        @Binding var attributedText: NSAttributedString
    
        @MainActor
        class Coordinator: NSObject, UITextViewDelegate {
            let textView = UITextView()
            
            var textDidChange: ((NSAttributedString) -> Void)?
            
            override init() {
                textView.isEditable = true
                textView.isSelectable = true
                super.init()
                textView.delegate = self
            }
            
            func textViewDidChange(_ textView: UITextView) {
                textDidChange?(textView.attributedText)
            }
        }
        
        func makeUIView(context: Context) -> UITextView {
            context.coordinator.textView
        }
        
        func makeCoordinator() -> Coordinator {
            Coordinator()
        }
        
        func updateUIView(_ uiView: UITextView, context: Context) {
            uiView.attributedText = attributedText
            context.coordinator.textDidChange = { attributedText = $0 }
        }
    
    }