Alex Pilugin
Alex Pilugin

Reputation: 703

Swift. Xcode 10.2.1. Error Thread 1: EXC_BAD_ACCESS (code=2,...) - Navigation between screens

I learn the book iOS Animations by Tutorials But I don't use Storyboard. I have several ViewControllers created programmatically. I have added RootVC in AppDelegate.swift. This application is working without navigation to the RootVC (going to the beginning) and the screens looks like that: All screens together

My question is about how to create such a navigation between different screens (ViewControllers) in Swift 4 (Xcode 10.2.1). It looks like there is an issue with looping... when the last ViewController instantiates the first RootVC and so on...

At the end I would like to have different custom navigation transitions on one ViewController (with .present() and with .navigationController?.pushViewController()

Navigation between screens 1 enter image description here

import UIKit


class FadePresentAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    let duration = 1.0
    var presenting = true
    var originFrame = CGRect.zero

    var dismissCompletion: (()->Void)?

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }

    //Setting the transition’s context
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        //Adding a fade transition
        let containerView = transitionContext.containerView
        let toView = transitionContext.view(forKey: .to)!
        containerView.addSubview(toView)
        toView.alpha = 0.0
        UIView.animate(withDuration: duration,
                    animations: {
                        toView.alpha = 1.0
        },
                    completion: { _ in
                        transitionContext.completeTransition(true)
        }
        )
    }        
}

import UIKit

//UIViewControllerTransitioningDelegate for self.present(self.nextScreen, animated: true, completion: nil)
//UINavigationControllerDelegate for self.navigationController?.pushViewController(self.nextScreen, animated: true)
class Screen3: UIViewController, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {


    let nextScreen = RootVC() //4th Screen //<----- EXEC ERRROR
    let transition = FadePresentAnimator()

    let btnSize:CGFloat = 56.0
    let btn1 = ClickableButton()

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "Screen 3"
        view.backgroundColor = HexColor.Named.BabyBlue

        self.navigationController?.delegate = self
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        setupLayout()
    }

    private func setupLayout() {
        //btn1:
        view.addSubview(btn1)
        btn1.setDefaultTitle(title: "▶") // ⏹ "▶" "■"
        btn1.apply(height: btnSize)
        btn1.apply(width: btnSize)
        btn1.applyDefaultStyle()
        if #available(iOS 11.0, *) {
            btn1.alignXCenter(to: view.safeAreaLayoutGuide.centerXAnchor)
        } else {
            // Fallback on earlier versions
        }
        if #available(iOS 11.0, *) {
            btn1.alignYCenter(to: view.safeAreaLayoutGuide.centerYAnchor)
        } else {
            // Fallback on earlier versions
        }
        btn1.clickHandler {
            self.nextScreen.transitioningDelegate = self
            //self.present(self.nextScreen, animated: true, completion: nil)
            self.navigationController?.pushViewController(self.nextScreen, animated: true)
        }
    }

    //forward
    func animationController(forPresented presented: UIViewController,
                            presenting: UIViewController, source: UIViewController) ->
        UIViewControllerAnimatedTransitioning? {
            return transition
    }

    //backward
    func animationController(forDismissed dismissed: UIViewController) ->
        UIViewControllerAnimatedTransitioning? {
            return nil
    }
}


//
//  AppDelegate.swift
//  Anime-Control-01
//


import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        //No Storyboards!
        window = UIWindow(frame:UIScreen.main.bounds)
        window?.makeKeyAndVisible()

        let rootVC = RootVC() //RootVC.swift
        let rootController = UINavigationController(rootViewController: rootVC)
        window?.rootViewController = rootController

        return true
    }

}

Upvotes: 0

Views: 736

Answers (1)

rodmytro
rodmytro

Reputation: 58

It looks like there is an issue with looping... when the last ViewController instantiates the first RootVC and so on...

Yeah, you're totally right. The thing is you're creating next ViewController right when current ViewController is initialized.

  1. The simplest way to fix this is to make nextScreen initialized lazily "on demand" by replacing this line

let nextScreen = RootVC() by this one lazy var nextScreen = RootVC()

  1. Or to create nextScreen variable right before the transition:

    btn1.clickHandler {
        let nextScreen = RootVC()
        nextScreen.transitioningDelegate = self
        navigationController?.pushViewController(nextScreen, animated: true)
    }
    

Upvotes: 1

Related Questions