JDev
JDev

Reputation: 5558

Fatal error when attempting to call a view controller function from an external modal view controller

Summary

I am currently faced with an issue that always worked fine in Objective-C, but I can't seem to get it to work in Swift (specifically v3). This is all built using Storyboards. I have two separate view controllers:

  1. ViewController: WKWebView & UIButtonItems
  2. SideMenuTableView: UITableView & UIButtons

Currently, the SideMenu pops over, modally, on top of the ViewController and lists a set of actions via UIButtons. I guess I'll just go off of the most simplistic example, just to get the idea down. What I am trying to accomplish in this example is reloading the WKWebView (ViewController) from a UIButton tap (SideMenuTableView). Below is a more clear picture of what I am trying to get at if things are still unclear:

Screenshots.png

Currently, I can call the SideMenu with a simple Storyboard segue (Kind: Present Modally). Furthermore, I can dismiss the SideMenu which was implemented into a simple close() function. However, when attempting to call the refresh function, I receive the following error message:

fatal error: unexpectedly found nil while unwrapping an Optional value

Code

import UIKit
import WebKit

class ViewController: UIViewController, UINavigationControllerDelegate, WKNavigationDelegate {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let webURL = URL(string: "https://google.ca")
        let webRequest = URLRequest(url: webURL!)
        webView.load(webRequest)

    func refresh() {
        webView.reload()
    }
}



import Foundation    

class SideMenuTableView: UITableViewController {

    @IBAction fileprivate func close() {
        self.dismiss(animated: true, completion: nil)
    }

    @IBAction func refresh(sender: AnyObject!) {
        ViewController().refresh()
        close()
    }
}

EDIT: Updated segue code.

// MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destinationViewController.
        // Pass the selected object to the new view controller.
        if let vc = segue.destination as? SideMenuTableView {
            vc.delegate = self
        }
    }
}

Upvotes: 0

Views: 484

Answers (1)

Emptyless
Emptyless

Reputation: 3050

You are initializing a new UIViewController with ViewController().refresh(). A delegate pattern is a nicer way to accomplish what you are trying:

Create a protocol:

protocol MainViewControllerDelegate /* Can be called whatever you like */ {
    func refresh()
}

And make your ViewController conform to it.

In your SideMenuTableView create a variable to hold the reference to it's delegates.

weak var delegate: MainViewControllerDelegate? 

In your prepareForSegue() function of ViewController. Check to see if the destination segue is your SideMenuTableView:

if let vc = destination.vc as? SideMenuTableView {
    vc.delegate = self
}

Now in your @IBAction button tap in SideMenuTableView call the delegate method.

@IBAction func refresh(sender: AnyObject!) {
        delegate?.refresh()
        close()
}

Because your ViewController conforms to this protocol (it has to have a refresh function in it's class), the refresh function will be executed.

Upvotes: 3

Related Questions