Search code examples
iosswiftsegue

Issue performing a segue when function called from another class


I have an Intro video section for an app. The storyboard has a segue "toMainMenu" that links to another storyboard view controller.

The IntroVideoVC has two classes inside it:-

class IntroVideoVC: UIViewController {
    var videoView: IntroVideoView!

    override func viewDidLoad() {
        videoView = IntroVideoView(controller: self)
        self.view.addSubview(videoView)

       let tap = UITapGestureRecognizer(target: self, action: #selector(tappedVideo))
       view.addGestureRecognizer(tap)
    }

    @objc func tappedVideo() {
        videoFinished();
    }

    func videoFinished() {
        self.performSegue(withIdentifier: "toMainMenu", sender: self)
    }   
}

class IntroVideoView: UIView {
    init(controller: UIViewController) {
        super.init(frame: CGRect(x: 0, y: 0, w: 0, h: 0))
        self.controller = controller
        ...
    }

    func handleVideo(videoPlayer: AVPlayer) {
        ...
        IntroVideoVC().videoFinished()
    }
}

If i tap the video it correctly performs the segue, however if i let the video end which in-turn calls IntroVideoViews -> handleVideo() method i get a crash stating it has no segue with identifier "toMainMenu".

I think i understand why but unsure how to get around the issue being new to swift. I believe it is because handleVideo() is using IntroVideoVC as a singleton and so is losing reference to IntroVideoVC's controller! I maybe (read: likely) be wrong but that is what i feel may be causing this issue.

Just looking for a nudge in the right direction

thanks in advance


Solution

  • You could use protocol to solve that, it would be something like this:

    protocol IntroVideoDelegate {
        func videoFinished()
    }
    class IntroVideoVC: UIViewController, IntroVideoDelegate {
        var videoView: IntroVideoView!
    
        override func viewDidLoad() {
            videoView = IntroVideoView(controller: self)
            videoView.videoDelegate = self
            self.view.addSubview(videoView)
    
           let tap = UITapGestureRecognizer(target: self, action: #selector(tappedVideo))
           view.addGestureRecognizer(tap)
        }
    
        @objc func tappedVideo() {
            videoFinished();
        }
    
        func videoFinished() {
           self.performSegue(withIdentifier: "toMainMenu", sender: self)
        }   
    }
    
    class IntroVideoView: UIView {
        videoDelegate: IntroVideoDelegate?
        init(controller: UIViewController) {
            super.init(frame: CGRect(x: 0, y: 0, w: 0, h: 0))
            self.controller = controller
            ...
        }
    
        func handleVideo(videoPlayer: AVPlayer) {
            ...
            videoDelegate.videoFinished()
        }
    }
    

    Remember that the videoView is still a subview to IntroVideoVC, so if the app goes back to this VC the user will see it, so if you don't want that to happen, be sure to remove it with:

    videoView.removeFromSuperview()
    

    In the videoFinished() func

    If you want to do running a VC func from the view you should get the parent of IntroVideoView, here is how: Given a view, how do I get its viewController? But i'm pretty sure it breaks MVC