Use Of IBOutlet in anotherView Controller?

I have create an app in swift where I'm having floating panel in bottom side, at the click event of floating panel button I want to change the label of the text, floating panel is in .xib file.

I'm showing my whole code with error below,

when btnPress (CardViewController 12 line ) action calles doNothing, at line (ViewController )31 I'm getting error of

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

ViewController

Code Block
import UIKit
class ViewController: UIViewController {
    enum CardState {
        case expanded
        case collapsed
    }
    var cardViewController: CardViewController!
    var visualEffectView: UIVisualEffectView!
    var cardHeight:CGFloat = 600
    let cardHandleAreaHeight:CGFloat = 65
    var cardVisible = false
    var nextState:CardState {
        return cardVisible ? .collapsed : .expanded
    }
    var runningAnimations = [UIViewPropertyAnimator]()
    var animationProgressWhenInterrupted:CGFloat = 0
    @IBOutlet weak public var txtlbl: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
        setupCard()
    }
    func doNothing(){
        txtlbl.backgroundColor =  colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)
        print("k")
    }
    func setupCard() {
        cardHeight = self.view.frame.height - 40
        visualEffectView = UIVisualEffectView()
        visualEffectView.frame = self.view.frame
        self.view.addSubview(visualEffectView)
        visualEffectView.isHidden = true
        cardViewController = CardViewController(nibName:"CardViewController", bundle:nil)
        self.addChild(cardViewController)
        self.view.addSubview(cardViewController.view)
        cardViewController.view.frame = CGRect(x: 0, y: self.view.frame.height - cardHandleAreaHeight, width: self.view.bounds.width, height: cardHeight)
        cardViewController.view.clipsToBounds = true
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleCardTap(recognzier:)))
        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ViewController.handleCardPan(recognizer:)))
cardViewController.handleArea.addGestureRecognizer(tapGestureRecognizer)
cardViewController.handleArea.addGestureRecognizer(panGestureRecognizer)
    }
    @objc
    func handleCardTap(recognzier:UITapGestureRecognizer) {
        switch recognzier.state {
        case .ended:
            animateTransitionIfNeeded(state: nextState, duration: 0.9)
        default:
            break
        }
    }
  
    @objc
    func handleCardPan (recognizer:UIPanGestureRecognizer) {
        cardHeight = self.view.frame.height - 40    
        switch recognizer.state {
        case .began:
            startInteractiveTransition(state: nextState, duration: 0.9)
        case .changed:
            let translation = recognizer.translation(in: self.cardViewController.handleArea)
            var fractionComplete = translation.y / cardHeight
            fractionComplete = cardVisible ? fractionComplete : -fractionComplete
            updateInteractiveTransition(fractionCompleted: fractionComplete)
        case .ended:
            continueInteractiveTransition()
        default:
            break
        }
    }
    func animateTransitionIfNeeded (state:CardState, duration:TimeInterval) {
        cardHeight = self.view.frame.height - 40
        if runningAnimations.isEmpty {
            let frameAnimator = UIViewPropertyAnimator(duration: duration, dampingRatio: 1) {
                switch state {
                case .expanded:
                    self.cardViewController.view.frame.origin.y = self.view.frame.height - self.cardHeight
                    self.visualEffectView.isHidden = false
                case .collapsed:
                    self.cardViewController.view.frame.origin.y = self.view.frame.height - self.cardHandleAreaHeight
                    self.visualEffectView.isHidden = true
                }
            }
               frameAnimator.addCompletion { _ in
                self.cardVisible = !self.cardVisible
                self.runningAnimations.removeAll()
            }
            frameAnimator.startAnimation()
            runningAnimations.append(frameAnimator)
            self.cardViewController.view.layer.cornerRadius = 12
        }
    }
    func startInteractiveTransition(state:CardState, duration:TimeInterval) {
        if runningAnimations.isEmpty {
            animateTransitionIfNeeded(state: state, duration: duration)
        }
        for animator in runningAnimations {
            animator.pauseAnimation()
            animationProgressWhenInterrupted = animator.fractionComplete
        }
    }
    func updateInteractiveTransition(fractionCompleted:CGFloat) {
        for animator in runningAnimations {
            animator.fractionComplete = fractionCompleted + animationProgressWhenInterrupted
        }
    }
    func continueInteractiveTransition (){
        for animator in runningAnimations {
            animator.continueAnimation(withTimingParameters: nil, durationFactor: 0)
        }
    }
}


Second ViewController (.xib)

Code Block
import UIKit
class CardViewController: UIViewController {
    var dash = ViewController()
    @IBOutlet weak var handleArea: UIView!
    @IBOutlet weak var btn: UIButton!
    @IBOutlet weak var uiViewColor: UIView!
    @IBAction func btnPress(_ sender: Any) {
        print("CardView Function Execute")
        dash.doNothing()
    }
}


I have tried protocols, segue method.

Help me with this,
Thank you in advance.



Accepted Answer
There is a problem with line numbering.
Line 67 is empty line (closing curly bracket)

But the problem is that you create a newController (dash) which points to nothing. And not the the existing viewController.

You may use protocols or segues

Protocols and delegation would better suit this need.
Could you show the code you tried with protocols ?

For segue :
  • create a segue between the 2 VC

  • create a property in the destination if you have some value to pass

  • in the sending VC, have a prepare for segue, where you load the destVC property with the value to pass.

  • or in the prepare call destVC.doNoting()

Code Block
@IBAction func btnPress(sender: UIButton!) {
self.performSegue(withIdentifier: "SegueID", sender: sender)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "SegueID" { // The ID of the segue you created in IB
if let destVC = segue.destination as? ViewController {
destVC.doNothing()
}
}
}

I have updated line numbers, can you please check it again?
You should not update a IBOutlet directly from segue ; you need to pass the value to a property in the DestVC (through prepare) and then use this value to update.

In your case, most likely problem is that you create a new controller but do not instantiate its IBOutlet with just calling ViewController().
You need to instantiate from storyBoard to get the IBOutlets.
Use Of IBOutlet in anotherView Controller?
 
 
Q