Home > Software engineering >  Timer crashing screens in SwiftUI
Timer crashing screens in SwiftUI

Time:07-19

I have an app that is using a timer to scroll programmatically between views on the onboarding screen. From the onboarding screen, I can go to login then signup screen (or profile then my account screen). In both ways, when opening the login screen and clicking on signup, the signup screen appears but then disappears and takes me back to login (The same happens when I try to enter My Account screen from the profile screen). Check please the gif attached.

I've searched all around the web without finding any similar issue.

Note: I am using NavigationLink to navigate between screens, and I tried also to use both Timer, and Timer.TimerPublisher leading me to the same result, the screens will crash once the timer fires.

Note: When removing the timer (or the code inside onReceive) everything works fine. I also suspect that the issue could be related to menuIndex

struct OnboardingView: View {

//MARK: - PROPERTIES

@State var menuIndex = 0
@State var openLogin = false
let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect()

//MARK: - BODY

var body: some View {
    
    NavigationLink(destination: LoginView(), isActive: $openLogin) {}
    
    VStack (spacing: 22) {
        
        ProfileButton(openProfile: $openLogin)
            .frame(maxWidth: .infinity, alignment: .trailing)
            .padding(.horizontal, 22)
            
            OnboardingMenuItem(text: onboardingMenu[menuIndex].title, image: onboardingMenu[menuIndex].image)
                .frame(height: 80)
                .animation(.easeInOut, value: menuIndex)
                .onReceive(onboardingVM.timer) { input in
                    if menuIndex < onboardingMenu.count - 1 {
                        menuIndex = menuIndex   1
                    } else {
                        menuIndex = 0
                    }
                }
            ...
        }
    }
}

CodePudding user response:

You can only have one detail NavigationLink, if you want more you have to set isDetailLink(false), this is why you are seeing the glitch. Alternatively if you don't require the split view behaviour you can use .navigationViewStyle(.stack). Note in iOS 16 this is replaced with NavigationStackView.

FYI a timer needs to be @State or it will start from zero every time that View is init, e.g. on a state change in the parent View causing its body to be invoked. Even better is to group the related properties into their own struct, e.g.

struct OnboardingViewConfig {
    var menuIndex = 0
    var openLogin = false
    let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect()

    // mutating func exampleMethod(){} 
}

struct OnboardingView: View {

    @State var config = OnboardingViewConfig()

    ...
  • Related