Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple instances in custom views are messing up the text state on macOS #15

Open
stefanomondino opened this issue May 15, 2022 · 8 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@stefanomondino
Copy link

Hello and first of all thank you for this great library :)

I'm trying to replicate something similar to Xcode tabs, with some kind of top "tab bar" selecting each code file I want to edit.

Since SwiftUI's TabView doesn't (currently) allow for tab bar customization, I've created my own using buttons in a HStack. When I click a button, the "main view" (containing the CodeEditor for selected file) changes and shows me file contents.

The bug I've found is very strange, after changing one of two tabs the global "state" starts to mixup, showing up either wrong file content (the previous one) or resetting contents after window loses focus or when my tab changes.

System's TextEditor seems to work fine.

I have a strong suspect this is somehow related to SwiftUI.

I hope attached video is "self explaining" (window always has focus, but something strange also happens when you focus any other application).

you can find it here https://github.com/stefanomondino/CodeEditor in the Demo folder.

Registrazione.schermo.2022-05-15.alle.14.54.31.mov

Also, it's worth nothing that also CodeEditorView (a very similar project) has the same issue.

Let me know if you have any idea, thanks!

@helje5
Copy link
Member

helje5 commented May 15, 2022

Are you actually switching the subviews, or just replace their contents?

@stefanomondino
Copy link
Author

@helje5 sorry didn't push the demo code to my fork ;)

This is my "SheetView"

https://github.com/stefanomondino/CodeEditor/blob/main/Demo/Sources/SheetView.swift

I'm passing my contents as a view builder

struct SheetView<Content: View>: View {

    @State private var currentIndex: Int? = nil
    
    let items: [SheetElement]
    @ViewBuilder let contents: (Int) -> Content
    
    init(items: [SheetElement], @ViewBuilder contents: @escaping (Int) -> Content) {
        self.items = items
        self.contents = contents
        currentIndex = items.isEmpty ? nil : 0
    }
    
    var body: some View {
        VStack(spacing: 0) {
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(alignment: .center, spacing: 0) {
                    ForEach(items.indices, id: \.self) { index in
                        SheetButton(index: index, selection: $currentIndex, item: items[index])
                        Divider()
                    }
                    Spacer()
                }
                .padding(0)
            }
            .frame(height: 30)
            Divider()
            if let index = currentIndex {
                contents(index)
            } else {
                Color(.clear)
            }
        }
        .layoutPriority(1)
    }
}

@stefanomondino
Copy link
Author

And this is how i use it (Editor is an observable object holding the binding)

let items = ["Test file 1", "Another test", "and yet again"].map { Editor.init($0)}
 SheetView(items: items) { index in
                    CodeEditor(source: items[index].binding,
                               language: .swift)
                   
                }

@helje5
Copy link
Member

helje5 commented May 15, 2022

Maybe try:

contents(index)
  .id(index)

This should create a new View for each index. (but even if that works, it is still a bug that should be fixed, not quite sure how/why this is happening).

@helje5 helje5 added the bug Something isn't working label May 15, 2022
@stefanomondino
Copy link
Author

stefanomondino commented May 15, 2022

@helje5 woah! and where did that id came from? :D

seems to work fine for the moment! Also you just teached me something really important I didn't know

Maybe it's worth adding a .id(UUID()) modifier to every CodeEditor view? Or maybe it's too much.

@helje5
Copy link
Member

helje5 commented May 15, 2022

No, that would recreate the backing View on every single run, not recommended (in general, not just here, the UUID thing is just a hack).

I still think the thing is a bug proper. Maybe diffing gets confused w/ representables.

@helje5 helje5 added the help wanted Extra attention is needed label May 15, 2022
@stefanomondino
Copy link
Author

stefanomondino commented May 15, 2022

From a very quick debug session on my project, seems like the source.projectedValue in the representable is keeping somehow the old value.

But from my point of view it makes total sense to add an .id to my custom tab view, it would be interesting to know if TextEditor and/or. TabView are doing something similar in their internal implementations.

Maybe it's something we can find out with Debug Memory Graph

@helje5
Copy link
Member

helje5 commented May 15, 2022

TabView has the tag, which presumably is similar to id.

As mentioned, the thing should work w/o the id and TextEditor can't attach an own id either as this is bound to the hierarchy. So I suspect TextEditor just does something right in the reload, which CodeEditor is doing wrong. No idea, needs debugging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants