user10051291
user10051291

Reputation:

Swift Nil exception with Button click event

I want to pass the data from one controller to another controller when user click the button by using segue . I data is coming from UITableViewCell in bother cases . In first view the data is loaded successfully from API but when I clicked the button to show the details ,It is showing following error .. Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.. on this line ... self.mc.movieTtile.text = dc.movieTitle.text!.. Although I mentioned that It will have the value with ! mark .

Here is the code for first view controller cell.

class MovieViewCell: UITableViewCell {
    
    static let identifier = "MovieViewCell"
    
    @IBOutlet weak var mainStackView: UIStackView!
    
    @IBOutlet weak var movieImage: UIImageView!
    
    @IBOutlet weak var movieTtile: UILabel!
    
    @IBOutlet weak var movieOverview: UILabel!
    

    
    
     func configureCell(title: String?, overview: String?, data: Data?) {
        
        movieTtile.text = title
        movieOverview.text = overview
        
        if let imageData = data{
            movieImage.image = UIImage(data: imageData)
        }
    }
    
}

Here is the code into view controller .

class NewViewController: UIViewController {
    
    var mc : MovieViewCell!
    var vc : ViewController!
    var dc: DeatilsViewCell!
    
    @IBOutlet weak var tableView: UITableView!
    private var presenter: MoviePresenter!
    @IBOutlet weak var name: UILabel!
    var finalname = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        name.text = "Hello: " + finalname
        setUpUI()
        
       // configure presenter
       presenter = MoviePresenter(view: self)
        presenter.getMovies()
    }
    
    
    @IBAction func showMovieDetails(_ sender: Any) {
        
        self.mc.movieTtile.text = dc.movieTitle.text!
        self.mc.movieOverview.text = dc.movieOverview.text!
        self.mc.movieImage.image = dc.movieImage.image
        
        // self.movieTitleDetail = mc.movieTtile.text!
         //self.movieOverviewDeatil = mc.movieOverview.text!
         //self.movieImageDetail = mc.movieImage.image
        
        performSegue(withIdentifier: "details", sender: self)
    }
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        var vc  = segue.destination as! DetailsViewController
        
        
        dc.movieTitle.text = mc.movieTtile.text
        dc.movieOverview.text = mc.movieOverview.text
        dc.movieImage.image = mc.movieImage.image
        
        /*vc.finalmovieTitle = self.movieTitleDetail
        vc.finalmovieOverview = self.movieOverviewDeatil
        vc.finalmovieImage = self.movieImageDetail*/
             
    }
    private func setUpUI() {
        tableView.dataSource = self
        tableView.delegate = self
    }
    
    @IBAction func change(_ sender: UISegmentedControl) {
            
            if sender.selectedSegmentIndex == 1{
            setUpUI()
            presenter = MoviePresenter(view: self)
            presenter.getMovies()
            }
        }
    }

extension NewViewController: MovieViewProtocol {
    
    func resfreshTableView() {
        tableView.reloadData()
    }
    
    func displayError(_ message: String) {
        let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
        let doneButton = UIAlertAction(title: "Done", style: .default, handler: nil)
        alert.addAction(doneButton)
        present(alert, animated: true, completion: nil)
    }
    
}

extension NewViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        presenter.rows
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: MovieViewCell.identifier, for: indexPath) as! MovieViewCell
        
        let row = indexPath.row
        let title = presenter.getTitle(by: row)
        let overview = presenter.getOverview(by: row)
        let data = presenter.getImageData(by: row)
        cell.configureCell(title: title, overview: overview, data: data)
        return cell
    }
    
}

extension NewViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }
}

Here is the code for second UIViewController Cell.

class DeatilsViewCell: UITableViewCell {

    
    
    static let identifier = "DeatilsViewCell"
    @IBOutlet weak var movieImage: UIImageView!
    
    @IBOutlet weak var movieTitle: UILabel!
    
    @IBOutlet weak var movieOverview: UILabel!
    
}

Here is the Second view Controller code to display the values from first view controller .

class DetailsViewController: UIViewController {
    
    var dc : DeatilsViewCell!
    var mc : MovieViewCell!
    
    @IBOutlet weak var tableView: UITableView!
    /*  var finalmovieTitle = ""
    var finalmovieOverview = ""
    var finalmovieImage: UIImage?*/
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        dc.movieTitle.text = mc.movieTtile.text
        dc.movieOverview.text = mc.movieOverview.text
        dc.movieImage.image = mc.movieImage.image
        
    
        
    }
    

    
}

Upvotes: -1

Views: 320

Answers (1)

Shehata Gamal
Shehata Gamal

Reputation: 100541

Here

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    var vc  = segue.destination as! DetailsViewController
    
    
    dc.movieTitle.text = mc.movieTtile.text
    dc.movieOverview.text = mc.movieOverview.text
    dc.movieImage.image = mc.movieImage.image
    
    /*vc.finalmovieTitle = self.movieTitleDetail
    vc.finalmovieOverview = self.movieOverviewDeatil
    vc.finalmovieImage = self.movieImageDetail*/
         
}

You set dc that's defined here var dc: DeatilsViewCell! which for sure has nil outlets as it's not loaded with IB , BTW this is not the way to pass data to other vc , you need to create vars/model in destination vc

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let vc  = segue.destination as! DetailsViewController 
    vc.finalmovieTitle = // string value please don't use mc
    vc.finalmovieOverview = // string value please don't use mc
    vc.finalmovieImage = // string value please don't use mc
}

Then inside DetailsViewController

var finalmovieTitle,finalmovieOverview,finalmovieImage = ""

Which should be used , also remove those

override func viewDidLoad() {
    super.viewDidLoad()
    
    dc.movieTitle.text = mc.movieTtile.text
    dc.movieOverview.text = mc.movieOverview.text
    dc.movieImage.image = mc.movieImage.image
     
}

From viewDidLoad and implement table data source methods to assign the sent variables

Warning :- You shouldn't declare table Cells as variables inside a vc they are meant to be used inside cellForRowAt no where else

Upvotes: 0

Related Questions