Simple Uitableview in Swift - Unexpectedly Found Nil

Simple UITableView in Swift - unexpectedly found nil

When you create a view in code, its IBOutlet properties don't get hooked up properly. You want the version that you get back from dequeueReusableCellWithIdentifier:

let cell = tableView.dequeueReusableCellWithIdentifier("BookCell") as BookTableViewCell

Swift UITableView reloadData() method unexpectedly found nil error

It looks like you are assigning ViewController class to both your first controller (which holds the table view) AND to your second controller (with the text field).

That's not going to work.

Add this class to your project, assign it as the "New Item" view controller's Custom Class, and connect the @IBOutlet and @IBAction:

class NewItemViewController: UIViewController {

// callback closure to tell the VC holding the table view
// that the Add button was tapped, and to
// "send back" the new text
var callback: ((String) -> ())?

@IBOutlet weak var textField: UITextField!

@IBAction func add(_ sender: Any) {
let item: String = textField.text!
callback?(item)
textField.text = ""
}

}

Next, change your ViewController class to the following:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var editButton: UIBarButtonItem!

var tableViewData = ["Apple", "Banana", "Orange", "Peach", "Pear"]

override func viewDidLoad() {
super.viewDidLoad()

// if you're not already seeing "Apple", "Banana", "Orange", "Peach", "Pear"
// add these two lines
//tableView.dataSource = self
//tableView.delegate = self
}

// MARK: Tableview methods

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableViewData.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = tableViewData[indexPath.row]
return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// print(tableViewData[indexPath.row])
}

// Allows reordering of cells
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}

// Handles reordering of cells
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let item = tableViewData[sourceIndexPath.row]

tableViewData.remove(at: sourceIndexPath.row)
tableViewData.insert(item, at: destinationIndexPath.row)
}

// Allow the user to delete cells
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCell.EditingStyle.delete {
tableViewData.remove(at: indexPath.row)
tableView.reloadData()
}
}

// MARK: IBActions

@IBAction func edit(_ sender: Any) {
tableView.isEditing = !tableView.isEditing

switch tableView.isEditing {
case true:
editButton.title = "Done"
case false:
editButton.title = "Edit"
}
}

// when "New Item" button is tapped, it will segue to
// NewItemViewController... set the callback closure here

// prepare for segue is called when you have created a segue to another view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

// error checking is always a good idea
// this properly unwraps the destination controller and confirms it's
// an instance of NewItemViewController
if let vc = segue.destination as? NewItemViewController {
// callback is a property we added to NewItemViewController
// we declared it to return a String
vc.callback = { item in
self.tableViewData.append(item)
self.tableView.reloadData()
self.navigationController?.popViewController(animated: true)
}
}
}

}

When you tap the "Add Item" button, we're assuming you have that connected to segue to the "New Item" view controller. By implementing:

override func prepare(for segue: UIStoryboardSegue, sender: Any?)

we will get a reference to the "New Item" view controller that is about to appear, and we'll assign it a "callback closure".

When we type some text and tap the "Add" button in the next controller, it will "call back" to the first controller, passing the newly typed text. That is where we'll update the data array, reload the table, and pop back on the navigation stack.

Unexpectedly found nil while unwrapping an optional value when loading tableView

I let it sit for a few days and I came back to realize a very dumb mistake I made. I working with around 15 view controllers right now and realized I had a duplicate of the one I posted above with the same name. I now understand why you say working with storyboards is very sticky. Though, I did not need it, I appreciate the help and I can say I learned a few things.

UITABLEVIEW Fatal error: Unexpectedly found nil while unwrapping an Optional value: when Core Data object is updated

Never retrieve cells from the table view force unwrapped. Don't do that. Be aware that a cell can be not on screen at the moment.

The most reliable way is to call only the methods to insert/delete/reload rows in the delegate method

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

switch type {
case .insert: tableView.insertRows(at: [newIndexPath!], with: .automatic)
case .delete: tableView.deleteRows(at: [indexPath!], with: .fade)
case .update: tableView.reloadRows(at: [indexPath!], with: .none)
case .move:
tableView.deleteRows(at: [indexPath!], with: .automatic)
tableView.insertRows(at: [newIndexPath!], with: .fade)
}
}

and handle configure somewhere else for example inside cellForRowAt

Error:Unexpectedly found nil while unwrapping an Optional value. Occurs when I assign a value to TextView which is part of UITableViewCell

Two issues:

  1. If the cell is designed as prototype cell in Interface Builder and you don't use an extra XIB you must not register the cell.
  2. If queryPosts() is supposed to populate the data source array you have to set delegate and datasource before calling the method.

    The best way is to connect both in Interface Builder and delete the two lines.

override func viewDidLoad()
{
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
queryPosts()
}

How to fix IBOutlet unexpectedly found nil error when binding data with RxSwift?

You have registered the cell class against your reuse identifier. This just instantiates your cell instances without any reference to your XIB file, and so the outlets are not connected.

You need to register the XIB file against the reuse identifier.


private func setupUI() { // Called in viewDidLoad()
resultsTv.register(UINib(nibName: "yourNib", bundle: nil), forCellReuseIdentifier: ResultCell.IDENTIFIER)
}

Label.text Error! - Unexpectedly found nil while implicitly unwrapping an Optional value

The problem is the protocol. You set delegate to an instance which is not the instance in the storyboard.

But with the given code you don't need the protocol at all.

Delete


protocol artistViewModelDelegate {
func loadArtistViewModel(data: ArtistViewModel)
}

...

var delegate: artistViewModelDelegate?

...

self.delegate = ArtistViewController()

and the protocol conformance, then replace

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
artistViewModel = ArtistViewModel(artist: data[indexPath.row])
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController

delegate?.loadArtistViewModel(data: artistViewModel!)
self.navigationController?.pushViewController(artistViewController, animated: true)
}

with

   func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let artistViewController = storyboard?.instantiateViewController(withIdentifier: "ArtistViewController") as! ArtistViewController

artistViewController.artistViewModel = ArtistViewModel(artist: data[indexPath.row])
self.navigationController?.pushViewController(artistViewController, animated: true)
}

And in ArtistViewController you have to use a temporary variable for the model because the outlets are not connected (yet) right after instantiation.

Replace the code in the question with

import UIKit

class ArtistViewController: UIViewController, artistViewModelDelegate {
@IBOutlet weak var artwork: UIImageView!
@IBOutlet weak var artistName: UILabel!
@IBOutlet weak var albumName: UILabel!

var artistViewModel : ArtistViewModel!

override func viewDidLoad() {
super.viewDidLoad()
artistName.text = artistViewModel.artistName
}
}


Related Topics



Leave a reply



Submit