Code Monkey home page Code Monkey logo

Comments (7)

ln-12 avatar ln-12 commented on August 17, 2024 1

I couldn't figure it out after some hours of debugging so I posted a question on StackOverflow. Hopefully, someone can figure it out as it would make your library perfect for my use case :)

from swiftui-navigation-stack.

matteopuc avatar matteopuc commented on August 17, 2024

Hi @ln-12, the problem here is due to the if-else statement in the SecondView. The transition between the ProgressView and the Text (there's always a transition in SwiftUI when you write an if-else like that) conflicts somehow with the screen transition. You can try to fix it by removing the else branch:

struct SecondView: View {
    @EnvironmentObject var viewModel: ViewModel

    var body: some View {
        VStack(alignment: .center) {
            HStack { Spacer() }
            Spacer()

            if(viewModel.someText.isEmpty) {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            }

            Text(viewModel.someText)

            Spacer()
        }
        .background(Color.red)
    }
}

from swiftui-navigation-stack.

ln-12 avatar ln-12 commented on August 17, 2024

Yeah, I already noticed that. Unfortunately, my real UI is much complexer (I have multiple else branches), so that this won't work.

So you think this is a SwiftUI problem and not specific to your library, right?

from swiftui-navigation-stack.

matteopuc avatar matteopuc commented on August 17, 2024

Well... basically yes. It depends on how SwiftUI manages the if-else transition in those cases. In your example you have two if-else transitions "overlapping" (the one involving the screens and the one involving the content of the SecondView). The second transition is not performed correctly. But it doesn't strictly depends on the navigation stack. Take a look at this simple example (it's basically your example without the navigation stack):

@main
struct MyApp: App {
    @StateObject private var viewModel = ViewModel()
    @State private var push = false
    private let transition = AnyTransition.asymmetric(insertion: .move(edge: .trailing),
                                                      removal: .move(edge: .leading))
    private let transitionAnimation = Animation.easeOut(duration: 3)

    var body: some Scene {
        WindowGroup {
            if !push {
                VStack(alignment: .center) {
                    HStack { Spacer() }
                    Spacer()

                    Button(action: {
                        push.toggle()
                        DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.2) {
                            DispatchQueue.main.sync {
                                self.viewModel.someText = "Test ### Test ### Test ### Test ### Test ### Test ###"
                            }
                        }
                    }){
                        Text("Go")
                    }

                    Spacer()
                }
                .background(Color.green)
                .transition(transition)
                .animation(transitionAnimation)
            } else {
                SecondView()
                    .transition(transition)
                    .animation(transitionAnimation)
                    .environmentObject(viewModel)
            }
        }
    }
}

final class ViewModel: NSObject, ObservableObject {
    @Published var someText: String = ""
}

struct SecondView: View {
    @EnvironmentObject var viewModel: ViewModel

    var body: some View {
        VStack(alignment: .center) {
            HStack { Spacer() }
            Spacer()

            if(viewModel.someText.isEmpty) {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            } else {
                Text(viewModel.someText)
            }

            Spacer()
        }.background(Color.red)
    }
}

As you can see the bug still occurs:

Jun-11-2021 18-41-46

We should try to fix the issue in this example (without the navigation stack) and then see if we can improve the navigation stack by integrating the fix directly into it.

from swiftui-navigation-stack.

ln-12 avatar ln-12 commented on August 17, 2024

I got the following working by using the TabView element. The big downside is that animations can't be customized, so only the default tab view page switching animation can be used. I am not sure why the transition is ignored and I can't get a working implementation of a custom TabViewStyle. It would be so much easier if Apple would open source stuff like Google does for Android (or simply provide a working native navigation).

@main
struct TransitionTestApp: App {
    @StateObject private var viewModel = ViewModel()

    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(viewModel)
        }
    }
}

struct ContentView: View {
    @EnvironmentObject var viewModel: ViewModel
    private let transition = AnyTransition.asymmetric(insertion: .move(edge: .top),
                                                      removal: .move(edge: .bottom))
    private let transitionAnimation = Animation.easeOut(duration: 3)
    
    var body: some View {
        VStack {
            TabView(selection: $viewModel.selectedTab) {
                FirstView()
                    .tag(0)
                    .transition(transition)
                    .animation(transitionAnimation)
                SecondView()
                    .tag(1)
                    .transition(transition)
                    .animation(transitionAnimation)
            }
            .onAppear(perform: {
               UIScrollView.appearance().bounces = false
             })
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            .transition(transition)
            .animation(transitionAnimation)
        }
    }
}


struct FirstView: View {
    @EnvironmentObject var viewModel: ViewModel
    
    var body: some View {
        VStack(alignment: .center) {
            HStack { Spacer() }
            Spacer()

            Button(action: {
                withAnimation {
                    viewModel.selectedTab += 1
                }
                DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.2) {
                    DispatchQueue.main.sync {
                        self.viewModel.someText = "Test ### Test ### Test ### Test ### Test ### Test ###"
                    }
                }
            }){
                Text("Go")
            }

            Spacer()
        }
        .background(Color.green)
    }
}

final class ViewModel: NSObject, ObservableObject {
    @Published var someText: String = ""
    @Published var selectedTab = 0
}

struct SecondView: View {
    @EnvironmentObject var viewModel: ViewModel

    var body: some View {
        VStack(alignment: .center) {
            HStack { Spacer() }
            Spacer()

            if(viewModel.someText.isEmpty) {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            } else {
                Text(viewModel.someText)
            }

            Spacer()
        }.background(Color.red)
    }
}

from swiftui-navigation-stack.

ln-12 avatar ln-12 commented on August 17, 2024

Using the SwiftUIPager library, it works as intended! I don't know why the content is looking correct in this library, but not in yours. Maybe you can have a look what they are doing different.

import SwiftUI
import SwiftUIPager

@main
struct TransitionTestApp: App {
    @StateObject private var viewModel = ViewModel()

    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(viewModel)
        }
    }
}

struct ContentView: View {
    @EnvironmentObject var viewModel: ViewModel

    var items = Array(0..<2)
    
    var body: some View {
        Pager(
            page: viewModel.page,
            data: items,
            id: \.self
        ) { index in
            if(index == 0) {
                FirstView()
            } else if(index == 1) {
                SecondView()
            }
        }
        .bounces(false)
        .draggingAnimation(.standard)
    }
}


struct FirstView: View {
    @EnvironmentObject var viewModel: ViewModel
    
    private let transitionAnimation = Animation.easeOut(duration: 3)
    
    var body: some View {
        VStack(alignment: .center) {
            HStack { Spacer() }
            Spacer()

            Button(action: {
                withAnimation(transitionAnimation) {
                    viewModel.page.update(.next)
                }
                DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.2) {
                    DispatchQueue.main.sync {
                        self.viewModel.someText = "Test ### Test ### Test ### Test ### Test ### Test ###"
                    }
                }
            }){
                Text("Go")
            }

            Spacer()
        }
        .background(Color.green)
    }
}

final class ViewModel: NSObject, ObservableObject {
    @Published var someText: String = ""
    @Published var page: Page = .first()
}

struct SecondView: View {
    @EnvironmentObject var viewModel: ViewModel

    var body: some View {
        VStack(alignment: .center) {
            HStack { Spacer() }
            Spacer()

            if(viewModel.someText.isEmpty) {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            } else {
                Text(viewModel.someText)
            }

            Spacer()
        }.background(Color.red)
    }
}

With this approach you also get the "swipe to go back" behavior discussed in issue #8

from swiftui-navigation-stack.

ln-12 avatar ln-12 commented on August 17, 2024

I did a quick implementation with your library here: https://github.com/ln-12/swiftui-navigation-stack/tree/animation-fix

With this approach, you have to set your rootView like this:

NavigationStackView(navigationStack: self.navigationStack) {}
.onAppear(perform: {
    self.navigationStack.push(RootView())
})

With that, I get the right push animations in all cases and also have the "swipe to go back" gesture. It's far from perfect, other animations might not work, but it is exactly want I was looking for. Maybe you find a way to properly integrate it or it might just help someone else who is looking for this functionality. You can also tell me how you would integrate it so that I can create a pull request :)

from swiftui-navigation-stack.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.