1

I have a UIView defined in

class InteractViewController: UIViewController {

    @IBOutlet weak var tipsView: UIView!
        var tipslayer: CALayer {
        return tipsView.layer
    }
}

There are a number of constraints also attached in the class as well as labels for example.

@IBOutlet weak var tips1Top: NSLayoutConstraint!
@IBOutlet weak var tips1Right: NSLayoutConstraint!
@IBOutlet weak var tipsTitle: UILabel!
@IBOutlet weak var tipsDesc: UILabel!

All are connected on the storyboard to their correct outlets. Referencing these within the actual InteractViewController itself I am able to adjust opacity, update labels on the layer etc.

However, these are not needed unless the user is first using the application so a simple if else on the InteractViewController calls a function to either display and update properties of these or not show them at all.

The function that controls and updates these properties is in another class (Swift file in the project called.

class TipsViewController: UIViewController {

    func tips() {
        InteractViewController().tipslayer.transform = CATransform3DMakeScale(0.8, 0.8, 1.0)
    }
}

Causes the app to crash creating

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

on

return tipsView.layer (the InteractViewController)

tipsView and tipslayer are of cause not optionals and are not declared as optionals.

I have three questions I hope I can get assistance with.

  1. Is having the physical reference to the outlets on the ViewController class InteractViewController: UIViewController the problem? If so is it because they are supposed to be programmatically added instead on the class TipsViewController: UIViewController (which does not have a physical visual view controller in Xcode)?

  2. Does having a physical reference to the outlets on the

    class InteractViewController: UIViewController

create unnecessary memory use if on InteractViewController the call to the function on the second ViewContoller TipsViewController.tips() is never called so in other words the answer to question 1 is essentially any physical outlets should be programmatically added on lass TipsViewController: UIViewController?

  1. If there is no memory issue and knowing that tipsView and the returning tipslayer are not optionals why am I simply unable to reference them from the tips() function in the other class (TipsViewController: UIViewController)? I have seen multiple posts about "passing data between view controllers" but I am not passing data between variables or arrays. I want/ need to simply update label string, constraints (objects in one view controller class from another view controller class.

I know people are busy so just to say in advance I appreciate all feedback and assistance with the problem.

It was suggested that my question was a duplicate of [What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean?

But this is not my question. MY question relates specifically to accessing an Outlet from another ViewController which is then generating a Nil response to the Outlet because as explained InteractViewController is apparently not referencing the ViewController. Please read the explanations and answer provided by Rob which answers this specific question.

  • Hi Rob and many thanks. So if I can summarise...basically the outlets should be on TipsViewController and that way InteractViewController can just call the function TipsViewController().tips() ? Would this then circumvent the problems? I'm thinking also especially since you say only the view controller itself should be trying to access its own outlets. – Scott James Dec 20 '18 at 15:37
  • Hi again Rob...sorry if I was unclear. And again thanks for all your advice to this point. Basically there is a viewcontoller (InteractViewContoller) it checks to see if a UserDefault has been previously set and if not shows a series of "TIPS" to the user. The tips UIView is on InteractViewController but all the transformation and changes to all the tips is on the TipsViewController. If the User.Default on InterractViewController has not been set (ie. its the users first time viewing the app) then InteractViewController calls TipsViewController().tips(). – Scott James Dec 20 '18 at 15:56
  • Having read what you have written so far it seems to me that the main problem is the way I have structured / created the logic. That the tips outlets should perhaps instead of being on InteractViewController only be added programmatically by TipsViewController in TipsViewController class if InteractViewController class calls TispViewController.tips()? – Scott James Dec 20 '18 at 15:56
  • Possible duplicate of [What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean?](https://stackoverflow.com/questions/32170456/what-does-fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-valu). Be sure to see [this answer](https://stackoverflow.com/a/49818897/1226963). – rmaddy Dec 20 '18 at 16:20

1 Answers1

0

Your title says "IBOutlet returns Fatal error: when referenced from another UIViewController class". The bottom line is that one view controller should never access another view controller's outlets.

In your code snippet, you employ the InteractViewController() syntax. Unfortunately, this just instantiates view controller, but does not hook up outlets in storyboard. You need to instantiate via storyboard and, even then, you can’t reference your outlets immediately, but only after viewDidLoad is called later. Only the view controller itself should be trying to access its own outlets, anyway.

The TipsViewController() or InteractViewController() syntax says "create a new, blank instance of that view controller". It's equivalent to saying TipsViewController.init() or InteractViewController.init(). And, worse, it's not using the storyboard at all (and thus no outlets will be hooked up). That's not what you want. You should avoid using this ViewController() syntax, as outlets will never been accessible that way.


InteractViewController should determine whether TipsViewController should be presented (e.g. checking user defaults), and if so, present it. The only thing InteractViewController should do, though, is:

  • figure out whether to present TipsViewController;
  • if so, present that TipsViewController; and
  • pass model data (e.g. whatever InteractViewController retrieved from UserDefaults) to the TipsViewController instance that is created for you.

But anything and everything about how TipsViewController presents its subviews, is the purview of TipsViewController alone. InteractViewController should never interact directly with any outlets of TipsViewController.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Hi Rob, again sincerest thanks. It is/ was as I suspected that the actual tips UIView and Constraints should be added on the TipsViewController and not the InteractViewController and so should be done programmatically since TipsViewController is not in the storyboard. It's not easy working alone and I have no one to tun towards so appreciate greatly all your input and your answer is accepted because I see it is correct!!! – Scott James Dec 20 '18 at 16:35