i'm trying to use a xib file as cell in a table view. but it says "unexpectedly found nil while unwrapping an Optional value" on registerNib. i think it's about referencing outlet but i don't know why it occurs.
my view is as follow:
as it shows, table view is inside Play Video View Controller.
my viewDidLoad is:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tableView.registerNib(UINib(nibName: "NewestTableViewCell", bundle: nil), forCellReuseIdentifier: "NewestTableViewCell")
}
and table view methods:
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return video.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : NewestTableViewCell = tableView.dequeueReusableCellWithIdentifier("NewestTableViewCell") as! NewestTableViewCell
let vd = video[indexPath.row]
cell.title.text = vd.title
cell.title.textAlignment = .Right
cell.duration.layer.borderColor = UIColor.whiteColor().CGColor
cell.duration.layer.borderWidth = 1.0
cell.duration.layer.cornerRadius = 3.0
cell.duration.clipsToBounds = true
cell.duration.text = vd.length
imageDl(vd.imageUrl){ image in
cell.videoImage.image = image
}
cell.time.text = vd.published
cell.time.textAlignment = .Right
cell.viewed.text = vd.view
cell.viewed.textAlignment = .Right
return cell
}
I used this nib in other TableViewControllers but when using in UIViewController which contains tableView, it'n not working. what reason causes this?
thanks
The only thing in the line that could be forcefully unwrapped is your tableView. Check its referencing outlet.
Related
Here's my DisruptionsViewController which has the tableView. the function setUp() is used in another ViewController class to set up the DisruptionsViewController.
public class DisruptionsInfoViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
public override func viewDidLoad() {
super.viewDidLoad()
self.setUp()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UINib(nibName: "DisruptionInfoTableViewCell", bundle: nil), forCellReuseIdentifier: "disruptionsInfoTableViewCell")
}
private func loadFromNib() -> UIView? {
let nibName = String(describing: DisruptionsInfoViewController.self)
let nib = UINib(nibName: nibName, bundle: Bundle(for: type(of: self)))
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
public func setUp() {
guard let disruptionsInfoViewController = self.loadFromNib() else { return }
disruptionsInfoViewController.frame = self.view.bounds
}
}
extension DisruptionsInfoViewController: UITableViewDataSource, UITableViewDelegate {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "disruptionsInfoTableViewCell", for: indexPath) as? DisruptonInfoTableViewCell else { return UITableViewCell() }
return cell
}
}
Here's the tableViewCell class.
import UIKit
class DisruptonInfoTableViewCell: UITableViewCell {
#IBOutlet weak var testLabel: UILabel!
}
I can see the tableView in the view debugger, but unable to see it in the view as the tableViewCell is not registered for some reason.
Here's how I am using it in another controller's delegate method
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let viewModel = presenter.headerViewModel(for: section) else { return nil }
let dateSummaryView = DateSummaryView(frame: .zero)
dateSummaryView.setup(with: viewModel)
let disruptionsViewController = DisruptionsInfoViewController()
return disruptionsViewController.view
}
Does anyone know where the problem could be?
I tried following tutorials from YouTube and other articles, they use the same approach but for some reason it doesn't work for me.
First, as mentioned in the comments, nothing in your setUp() is doing anything, so it can be removed.
The reason you are not seeing your table view rows in your other table's section header views is because here (I'll ignore the first three lines since they have nothing to do with this):
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//guard let viewModel = presenter.headerViewModel(for: section) else { return nil }
//let dateSummaryView = DateSummaryView(frame: .zero)
//dateSummaryView.setup(with: viewModel)
let disruptionsViewController = DisruptionsInfoViewController()
return disruptionsViewController.view
// as soon as we return, disruptionsViewController is released
// and no code it contains will be executed
}
You created an instance of DisruptionsInfoViewController, pulled out its view, and then tossed away the controller code.
If you want to use this approach (rather odd, but we have to assume you have a logical reason to do this), you need to keep a reference to DisruptionsInfoViewController so its code can be used:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let disruptionsViewController = DisruptionsInfoViewController()
// add disruptionsViewController as a child view controller
// this will "hold on to it" so its code can execute
addChild(disruptionsViewController)
disruptionsViewController.didMove(toParent: self)
return disruptionsViewController.view
}
Now, this is technically incorrect, as Apple's docs state:
Call the addChildViewController: method of your container view controller.
Add the child’s root view to your container’s view hierarchy.
Add any constraints for managing the size and position of the child’s root view.
Call the didMoveToParentViewController: method of the child view controller.
But, because we are returning the view for use as a section header view, we cannot perform 2. before calling didMove(toParent: self).
You didn't include in your post (or mention in your comments) how you're setting the Height of the section header, so, using:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150.0
}
We get this with your original code - section header view background color is blue, the "table view in header view" background color is green:
and we get this when using addChild():
Here is a complete example project: https://github.com/DonMag/Disrupt
While this will work, if you really want to embed a table view in another table view's section header(s), there are better ways to do it.
I am trying to implement a UISearchController (not UISearchDisplayController which is deprecated)
I face a ridiculously time eating issue.
When I try to dequeueResusableCellWithIdentifier it doesn't works with my CellAutocompletion (UITableViewCell subclassing)
Like this :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let item = filteredProducts[indexPath.row]
let cell:CellAutocompletion = self.tableView.dequeueReusableCellWithIdentifier("suggestionCell") as! CellAutocompletion
cell.itemLabel?.text = item.item_name;
return cell
}
This throw me fatal error: unexpectedly found nil while unwrapping an Optional value
BUT when I do this :
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "suggestionCell");
It works. So with a Default iOS cell, it works, with my custom cell not. Any idea?
I think I ask the wrong tableview but considering I am inside a TableviewController included inside UISearchController which is used by another VC, I am lost.
My main VC which instanciate my searchController and the TableViewController where I have issue.
//ViewDidLoad
resultsTableController = ProductResultTabController();
resultsTableController.tableView.delegate = self;
self.searchController = UISearchController(searchResultsController: resultsTableController);
searchController.searchResultsUpdater = self;
searchController.dimsBackgroundDuringPresentation = true;
searchController.searchBar.sizeToFit()
tableView.tableHeaderView = searchController.searchBar
My CustomCell Class
class CellAutocompletion:UITableViewCell{
#IBOutlet weak var itemLabel: UILabel!
#IBOutlet weak var parentItemLabel: UILabel!
}
ProductResultTabController (subclass of TableViewController)
class ProductResultTabController: UITableViewController {
var filteredProducts = [ITEM]();
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredProducts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let item = filteredProducts[indexPath.row]
let cell:CellAutocompletion = self.tableView.dequeueReusableCellWithIdentifier("suggestionCell") as! CellAutocompletion!
cell.itemLabel?.text = item.item_name;
return cell
}
}
As I understand, your ProductResultTabController lives in the storyboard with the cell prototype. As a consequence, call resultsTableController = ProductResultTabController() doesn't do the most essential part of creating a TableViewController, which is registering your tableView: for a class or nib. You could create your cell in a nib and call tableview.registerNib: in your PRTC viewDidLoad:, but there is an other, slightly more convenient way:
Create your PRTC in the storyboard and prototype the cell (I believe you've already done that)
In SB, go to attributes inspector and give the controller a storyboard ID
3 - In your main VC's viewDidLoad:, replace resultsTableController = ProductResultTabController()`by the following:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
resultsTableController = storyboard.instantiateViewControllerWithIdentifier(myStoryboardID)
I'm using Xcode 7.0, Swift 2
I'm basically trying to create a custom class that will build a UITable, then in the ViewController I make a new object of the table and load it into self.view;
The problem I'm having is that the function func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell isn't being called at all from within the custom class. I've been looking for a solution for 3 days now and I've tried rebuilding the App and code several times with no luck.
Please note, if I use the same code (that is everything required to build the table; excluding init functions, etc) in the ViewController.swift file, it works fine.
I know the problem is with the cellForRowAtIndexPath function because it will not print out the statement I set in that block of code when it runs. All other functions are called, but for some reason this isn't being called. Not sure if I overlooked something here. Any help would be appreciated. Thanks in advance.
class sideTest: NSObject, UITableViewDelegate, UITableViewDataSource {
let tesTable: UITableView = UITableView()
var items: [String]?
var mView: UIView = UIView()
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("The number of rows is: \(self.items!.count)")
return self.items!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("\nLets create some cells.")
let sCell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
sCell.textLabel?.text = self.items![indexPath.row]
sCell.textLabel?.textColor = UIColor.darkTextColor()
return sCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
}
func tblSetup() {
self.tesTable.frame = CGRectMake(0, 0, 320, mView.bounds.height)
self.tesTable.delegate = self
self.tesTable.dataSource = self
self.tesTable.backgroundColor = UIColor.cyanColor()
// load cells
self.tesTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
self.tesTable.reloadData()
print("Currenlty in tblSetup.\nCurrent rows is: \(self.items!.count)")
}
//Init
override init() {
super.init()
self.items = nil
self.tblSetup()
}
init(sourceView: UIView , itemListAsArrayString: [String]) {
super.init()
self.items = itemListAsArrayString
self.mView = sourceView
self.tblSetup()
}
}
Here is the code from ViewController.swift; Please do note that the table gets built, but the cells do not populate, even if I manually enter cell info by doing: sCell.textLabel?.text = "test cell"
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
self.view.addSubview(myTable.tesTable)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Again, any help is greatly appreciated. Thanks.
Your view controller don't have a strong reference to your sideTest var.
Once your view did load finished,your sideTest is nil.Although you have a tableview(by add subview), but you no longer have a data source.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {}
is called after view did load. That cause the problem.
change your view controller to:
var tb :sideTest?
override func viewDidLoad() {
super.viewDidLoad()
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
print(myTable.tesTable.frame)
tb=myTable
self.view.addSubview(myTable.tesTable)
}
change your cellforrowatindexpath to:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("create cells")
var cell :UITableViewCell?
if let sCell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("cell"){
cell=sCell
}else{
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
cell!.textLabel?.text = self.items![indexPath.row]
cell!.textLabel?.textColor = UIColor.darkTextColor()
return cell!
}
this will fix most of the problems.
Your code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let myTable: sideTest = sideTest(sourceView: self.view, itemListAsArrayString: ["Cell 1", "Cell 2", "Cell 3"])
self.view.addSubview(myTable.tesTable)
}
I would think that the myTable variable goes out of scope and is released when viewDidLoad finishes, so there is no data source or delegate after that. Did you verify that the self.view.addSubview(myTable.tesTable) retains it? Try moving declaration of myTable outside of the function level (to property level) or add a diagnostic print to deinit..
I want to create three cells in a tableview controller and I added three prototype cells in storyboard it doesn't crash but it view an empty tableview.
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
let nib1 = UINib(nibName: "one", bundle: nil)
tableView.registerNib(nib1, forCellReuseIdentifier: "one")
let nib2 = UINib(nibName: "two", bundle: nil)
tableView.registerNib(nib2, forCellReuseIdentifier: "two")
let nib3 = UINib(nibName: "three", bundle: nil)
tableView.registerNib(nib3, forCellReuseIdentifier: "three")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 3
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
var cell:UITableViewCell!
switch (indexPath.row){
case 0 :
cell = tableView.dequeueReusableCellWithIdentifier("one") as! one
cell.textLabel?.text = "book1"
break;
case 1:
cell = tableView.dequeueReusableCellWithIdentifier("two") as! two
cell.textLabel?.text = "book2"
break;
case 2 :
cell = tableView.dequeueReusableCellWithIdentifier("three") as! three
cell.textLabel?.text = "book3"
default:
break;
}
// Configure the cell...
return cell
}
Since you'll always want exactly 3 cells to show in your table view, you should use the "Static Cells" style of UITableView. You can set this in the Storyboard if you select the appropriate UITableView and look in the Attributes Inspector.
Then, drag out the correct number of cells and configure them in the Storyboard. Connect each cell to an IBOutlet if you need to manipulate them or their contents programmatically.
Note that, since static table views are only allowed in a UITableViewController, you may need to change the class that contains the code you included in your question.
EDIT Answer below in this post
I'm trying to set up a UITableView controller in storyboard, with a separate datasource, and I've hit a wall. The data source doesn't seem to respond to changes or push it's 'updates' to the table view. I've tried implementing the data source in the MainMenuTableViewController which worked fine.
This is my MainMenuTableViewController
override func viewDidLoad() {
super.viewDidLoad()
sharedLightsManager.delegate = self
sharedLightsManager.loadNetworkContext()
dataSource = MainMenuTableViewDataSource(sharedLightsManager: sharedLightsManager)
tableView.dataSource = dataSource
tableView.delegate = dataSource
title = "test"
}
//This method fires each time a change happens
func updateLights(){
lights = sharedLightsManager.localNetworkContext.allLightsCollection.lights
tableView.reloadData()
}
MainMenuDataSource:
class MainMenuTableViewDataSource: NSObject, UITableViewDataSource, UITableViewDelegate
{
let reuseIdentifier = "tableViewCell"
var sharedLightsManager: SharedLightsManager?
var lights = []
init(sharedLightsManager: SharedLightsManager)
{
self.sharedLightsManager = sharedLightsManager
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lights.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("tableViewCell", forIndexPath: indexPath) as UITableViewCell
var lights = sharedLightsManager!.localNetworkContext.allLightsCollection.lights
var light = LFXLight()
if lights.count == 0 {
println("Lights array still loading...")
} else {
light = lights[indexPath.row] as LFXLight
}
return cell
}
}
and here is my outlets:
I've just figured it out. A bit embarrassing. It was due to the lights array not having any objects in it, so obv. lights.count would return 0, therefore no rows...
The data source will not push updates unless the UITableView is told to reloadData. If you change the numberOfRows value, it will not update unless the tableView is notified through methods like insertRowAtIndexPath, reloadData, deleteRowAtIndexPath etc.