danielsaidi / RichTextKit

RichTextKit is a Swift SDK that helps you use rich text in Swift and SwiftUI.
MIT License
950 stars 126 forks source link

`onChange(of:perform)` not being called for bound `NSAttributedString` when the editor is typed into #64

Open biajoeknee opened 1 year ago

biajoeknee commented 1 year ago

I'm trying to get an action to occur whenever the text of the editor changes however when I add an onChange(of:perform) callback to the editor view it never gets called. The only time it is actually called is if the @State field is mutated directly from within the view that defines it.

DavidAlvarezDev commented 1 year ago

Not sure if this helps, but it looks like the onChange modifier is called before the bound text is updated. I found this out by troubleshooting an issue I had today with the editor.

What property are you trying to observe? You might look into adding a didSet { } somewhere on the property depending on your setup.

biajoeknee commented 1 year ago

Yeah, that's always the case with onChange. I'm trying to observe the actual text that gets bound to RichTextEditor, but whenever the text is mutated — via the editor — the onChange callback is never called — despite the value actually mutating.

Tried didSet, and it was the same deal — not being called despite the NSAttributedString property being mutated by RichTextKit.

danielsaidi commented 1 year ago

Thanks for reporting @bee-uh-joe-knee - I will take a look at this.

danielsaidi commented 1 year ago

@biajoeknee @DavidAlvarezDev

onChange(of:) works for me in the demo, using the following code:

struct EditorScreen: View {

    init() {
        // RichTextEditor.standardRichTextFontSize = 100
    }

    @State
    private var text = NSAttributedString.empty

    @StateObject
    var context = RichTextContext()

    var body: some View {
        VStack {
            editor.padding()
            toolbar
        }
        .background(Color.primary.opacity(0.15))
        .navigationTitle("RichTextKit")
        .toolbar {
            ToolbarItemGroup(placement: .navigationBarTrailing) {
                MainMenu()
            }
        }
        .viewDebug()
        .onChange(of: text) { newValue in
            print(newValue)
        }
    }
}

Typing in the text field prints every change.

I'm closing this. Feel free to reopen if you still don't get it to work.

asabhaney commented 9 months ago

I might be nuts, but I'm encountering this issue as well. On iOS it seems to work fine, but not on macOS. I've also tried to isolate this issue by creating a new NSViewRepresentable from scratch that wraps around NSTextView, and the onChange doesn't seem to fire there either unless a new NSAttributedString instance is assigned to the Binding after each change (which causes other problems).

biajoeknee commented 9 months ago

yeah, it's been a while and haven't touched the project that was originally using this, but i too was building for macOS when running into this issue

chrisdmacrae commented 9 months ago

This bug sucks! :( makes RichTextKit unusable for MacOS. I can reproduce using the demo project.

@danielsaidi

danielsaidi commented 9 months ago

Hi all! I can confirm that I can also see that the print doesn't work on macOS. It's very strange, because the string does in fact change.

I tried adding this to the demo app, instead of the code above:

struct EditorScreen: View {

    ....

    var body: some View {
        VStack(spacing: 0) {
            Divider()
            HStack(spacing: 0) {
                editor
                Divider()
                toolbar
            }
        }
        .toolbar {
            ToolbarItem(placement: .automatic) {
                RichTextAction.ButtonStack(
                    context: context,
                    actions: [.undo, .redo, .copy]
                )
            }
        }
        .viewDebug()
        .onChange(of: context.selectedRange) { _ in   <--- HERE
            print(text.string)
        }
    }
}

Subscribing to the selected range DOES work, and printing the text correctly prints the current text.

@asabhaney @biajoeknee @chrisdmacrae Can you confirm if this works for you? I have pushed this change to the main branch, so you can test it without having to make any modifications.

Kinark commented 5 months ago

I can confirm onChange doesn't detect anything on macOS.

I got to make it work by using .onReceive like so:

        .onReceive(context.objectWillChange) { _ in
            // Do whatever you wanna do
        }

It'll be called much more times than needed, but it works.

S1D1T1 commented 5 months ago

I do use .onChange with RTK successfully, for syntax coloring. My observer is set up like so:

.onChange(of:context.attributedString.string){
...
  }

edit: -with both macOS and iOS