1

I'm having a problem, I have a UIButton in a UIViewController class and I want to enable that button after an animation that happens in a UIView class that is in another file.

class MainViewController: UIViewController {
    @IBOutlet weak var nextButton: UIButton! 

    @IBAction func nextButtonPressed(sender: UIButton) {
        nextButton.enable = false
    }
}

When I try to call the nextButton from the viewController class after the animation is done I get this error:

EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode = 0x0)

I get the error on the line where I set the nextButton enable to true.

class CustomView: UIView {
    var vc = MainViewController()

    func animationEnded() {
        vc.nextButton = true
    }
}

I don't have a clue what I'm missing and I would appreciate some help. Thanks

pableiros
  • 14,932
  • 12
  • 99
  • 105
  • What's the complete, human readable error message? And which line exactly is causing the error? – rmaddy Aug 24 '16 at 19:30
  • fatal error: unexpectedly found nil while unwrapping an Optional value. I get the error inside the animationEnded function – Giorgio Doganiero Aug 24 '16 at 19:36
  • maddy: This is really a much deeper question that deserves a separate answer, about communicating across different VCs – BaseZen Aug 24 '16 at 19:40
  • @GiorgioDoganiero They don't like how you asked. Instead ask a new question with a better title: "How do I send a message to a UIViewController in a separate file from a custom UIView?" Your error is incidental to the issue. Remove your buggy code and error message, which is apparently confusing one of the moderators into incorrectly closing this question, or show your understanding that all @IBOutlets are unusable ( `nil` ) in your incorrect approach. – BaseZen Aug 24 '16 at 19:44

2 Answers2

0

Make a delegate in your UIView that tells when it should hapen

protocol CustomViewDelegate {
    func pushThatButton()
}

in CustomView class put this:

weak var delegate: CustomViewDelegate?

then

func animationEnded() {
    delegate.pushThatButton()
}

and in UIViewController

class MainViewController: UIViewController, CustomViewDelegate {

and implement delegate ofc

func pushThatButton()
    nextButton.sendActionsForControlEvents(.TouchUpInside)
}

almost forget, do an outlet to your view in viewController and setup delegate! in viewDidLoad() or when you will find this best

customViewOutlet.delegate = self
Lu_
  • 2,577
  • 16
  • 24
0

You encounter an error because in your CustomView you create a new MainViewController, you're not using the one initialized from the storyboard. That new MainViewController doesn't have any of its properties initialized, so nextButton is nil, hence the crash when you try to access it.

What you want to do is notify your controller from the view that the animation has ended so that the controller can update the button (since the controller owns the button). The standard way to do this in Cocoa is to use the delegate pattern like so:

class MainViewController: UIViewController, CustomViewDelegate
{
    @IBOutlet weak var nextButton: UIButton!
    @IBOutlet weak var customView: CustomView!

    @IBAction func nextButtonPressed(sender: UIButton) {
        self.nextButton.enabled = false
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        self.customView.delegate = self
    }

    func customViewAnimationDidEnd(customView: CustomView) {
        self.nextButton.enabled = true
    }
}

protocol CustomViewDelegate : class
{
    func customViewAnimationDidEnd(customView: CustomView)
}

class CustomView: UIView
{
    weak var delegate: CustomViewDelegate? = nil

    func animationEnded() {
        self.delegate?.customViewAnimationDidEnd(self)
    }
}

In this implementation the controller is the view delegate and gets notified when interesting events happen in the view (like a particular animation ending).

deadbeef
  • 5,409
  • 2
  • 17
  • 47