Error when trying to reload data in UITableView in Swift - ios

Still not solved!
I have been stuck with this problem way too long, so i hope you can help.
I Have two UITableViews side-by-side in one view in Storyboard. Each in a ContainerView to control their positions.
The idea is, if you touch a row in the first UITableView. The data from that row should be added to the second UITableView.
In TableOne i'm calling the function addDataToTableView() in didSelectRowAtIndexPath.
In Tabletwo addDataToTableView() get's the touched element and add it to the testData2 array. This works fine. The print() function spits out the right element.
But then on self.tableTwo.reloadData() the application crash with this error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
I’m not entirely sure what causes the Error, but i think it’s either that i don’t get the right instance of the class, created by the storyboard or it’s something with optimals.
i tried putting in some ??? and som !!! and i have tried wrapping .reloaddata() ind another thread. but it didn’t help.
This is the entire code.
Hope you can help :)
TableOne
class TableOne: UITableViewController {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var tableTwo = TableTwo()
var testData = ["test1","test2", "test3", "test4", "test5", "test6"]
override func viewDidLoad() {
super.viewDidLoad()
tableTwo = mainStoryboard.instantiateViewControllerWithIdentifier("tableTwoId") as! TableTwo
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = testData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableTwo.addDataToTableView(testData[indexPath.row])
}
}
TableTwo
class TableTwo: UITableViewController {
#IBOutlet var tableTwo: UITableView!
var testData2 = ["Test"]
func addDataToTableView(data: AnyObject) {
testData2.append(data as! String)
print("This works fine \(testData2[testData2.count-1] )")
self.tableTwo.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testData2.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = testData2[indexPath.row]
return cell
}
}

If you want to send data from One TableViewController either use prepareForSegue or didSelectRowAtIndexPath.
This line let cell = UITableViewCell() you only declare an instance of a UITableViewCell by you never called the method which makes that cell reusable in the queue.
class TableOne: UITableViewController
{
var testData = [String]()
override func viewDidLoad()
{
super.viewDidLoad()
testData = ["test1","test2", "test3", "test4", "test5", "test6"]
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return testData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath)
cell.textLabel?.text = testData[indexPath.row]
return cell
}
"CellIdentifier" is the name of the cell identifier from the tableView cell prototype into the storyboard
To send Data the TableView lets say we are using prepareForSegue and you must know that When the user selected one cell only that cell information will be sent to your next ViewController.
if segue.identifier == "NAME_OF_THE_SEGUE_IDENTIFIER"
{
let destination = segue.destinationViewController as("nameOftheTableView")
let indexPath = mytableview.indexPathForSelectedRow!
var dataToTransfer = testData[indexPath.row]
// let's say you have a string variable into your next ViewContrller called receiver
destination.receiver = dataToTransfer //<-- from the selectRow we assign that value into our next Controller.
}
So Build up on that to fix your code :)

Related

Add a row before rendering tableview swift 2

I am trying to add a row into my tableview before the tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int method is called. I tried to do it into the viewDidLoad() method without success is it possible ? and how ?
Here is my code :
import UIKit
class CustomTestViewController : UITableViewController {
#IBOutlet var formDetailTableView: UITableView!
var data: Data?
var firstData: FirstData?
struct CurrentFormTableView {
struct CellIdentifiers {
static let MyCell = "MyCell"
static let MyFirstCell = "MyFirstCell"
}
}
override func viewDidLoad() {
super.viewDidLoad()
//HERE ADD THE FIRST ROW ?
let cell = tableView.dequeueReusableCellWithIdentifier(CurrentFormTableView.CellIdentifiers.MyFirstCell, forIndexPath: indexPath) as! MyFirstCell
cell.displayCell(firstData)
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(CurrentFormTableView.CellIdentifiers.MyCell, forIndexPath: indexPath) as! MyCell
cell.displayCell(data[indexPath.row])
return cell
}
}
If I understand your question properly this problem can be easily :)
You always wants to show a first cell in your tableView no matter whether there is a data in your data array or not :) and your problem is you cant add the first object to data array thats all right buddy :)
Here is a solution :)
Don't do anything in ViewDidLoad :) Simply save your first row data object in your local variable lets say : yourCustomObject
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count + 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : myTestCell = tableView.dequeueReusableCellWithIdentifier("testCell")! as! myTestCell
if indexPath.row == 0 {
cell.nameLabel.text = yourCustomObject.property
}
else {
cell.nameLabel.text = data[indexPath.row -1].property
}
return cell
}
Problem solved :) Happy coding :)
How It works :
Simple,
Assume your data array is empty :) then it will return count as 0 :) But you always want to show your first cell isn't it :) so add +1 to data array count :) return data.count + 1
Now handle it carefully in cellForRowAtIndexPath. You dont want to end up accessing data in data array for first object so check for indexpath 0.
And you dont want to end up accessing object out of index of data so use data[indexPath.row -1]
Hope I made my point clear :) Happy coding
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count + 1 //Add plus 1 for firstData
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(CurrentFormTableView.CellIdentifiers.MyCell, forIndexPath: indexPath) as! MyCell
if indexPath.row == 0 {//First index will be firstData
cell.displayCell(firstData)
} else { //All other cell's will be filled with data array
cell.displayCell(data[indexPath.row - 1]) //Make sure you offset indexPath.row by 1 so you start at index 0 of data array
}
return cell
}

array index out of bounds when UITableViewCell tapped, with and without a didselectrowatindex method

Like Title
More information - I am populating my view controller with
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "showCalendars" {
// Setup new view controller
print("happening")
let vc = segue.destinationViewController as! CalendarSelectionViewController
for item in self.approvedCalendars {
vc.sentCalendars.append(item)
}
vc.tableView.reloadData()
vc.calendarDelegate = self
}
}
and my view controller code looks like :
import UIKit
import EventKit
class CalendarSelectionViewController: UITableViewController {
var sentCalendars: [EKCalendar]! = []
var calendarDelegate: selectCalendarDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "My Calendars"
//tableView.tableFooterView = UIView()
tableView.reloadData()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.sentCalendars.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("calendarCell", forIndexPath: indexPath)
cell.textLabel?.text = sentCalendars[indexPath.row].title
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 60
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
}
}
whenever i click on any uitableview cell, my application crashes and I receive "fatal error: Array index out of range".
I've tried printing out the indexPath.row in the didSelectRowAtIndexPath method and it prints the correct index, so why is this crashing? Is there a memory leak somewhere and the tableview is not showing the proper information? Thanks for your help.
Investigated my storyboard, I had an unwind segue that wasn't being used and was causing a fatal error: array index out of bounds. Thanks for the help!

Why cellForRowAtIndex doesn't run at all?

I've added TableView to my controller and created custom UITableViewCell. Later, I've tried to run this code:
func tableView(educationTableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> educationCell {
let cell:educationCell = educationTableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! educationCell
print("TableView runs")
return cell
}
but I do not get my print in logs TableView runs. I do not understand what happens and why it doesn't run. Who knows, why this problem appears?
I read other answers in SO, but noone helps =/
need the return value of numberOfRowsInSection greater than 0
Just make sure you have implemented proper DataSource and Delegate for controller, number of rows is greater then 0
import UIKit
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var items: [String] = ["We", "Heart", "Swift"]
#IBOutlet var tableView: UITableView! //hook this to your storyboard tableview
// MARK: - Controller life cycle stack
override func viewDidLoad() {
super.viewDidLoad()
//confirm table's delegate and data source programmatically
tableView.delegate = self;
tableView.dataSource = self;
}
//table view delegate and data source methods
func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
//number of rows must be greater then 0 to get called "CellForRowAtIndex"
return self.items.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! CraditCardCell
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
print("You selected cell");
}
}
One reason could be because you modified the return type to educationCell, instdead of the standard UITableViewCell. Try changing that.

How can I choose multiple rows and send those options to a second UITableView through a prepareForSegue function?

I'm working in a UITableView which contains 5 rows ("a" to "e"), and when I click on one of the rows, it shows me in the next UITableView the letter I chose as a Header (with the prepareForSegue function). Actually, until this point it works perfectly.. BUT when I try to add more "headers" in the 2nd tableView after clicking in more than one row from the first UITableView, it just show me the last option (for example, if I click first in "a", then "d" and then "c"... the second UITableView just show me the row "c" as a header, not the other 2 rows).
How can I choose multiple rows and send those options to a second UITableView through a prepareForSegue function?
My line of codes for the first view are these:
let array = ["a", "b", "c", "d", "e"]
#IBOutlet var initialTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueA" {
if let destination = segue.destinationViewController as? Content {
if let selectedRow = initialTableView.indexPathForSelectedRow?.row {
destination.title = array[selectedRow]
}
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.array.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellA", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = array[indexPath.row]
return cell
}
The second view contains these lines:
var contentArray:[String] = []
#IBOutlet var contentTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
contentArray.append(title!)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return contentArray.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellContent = tableView.dequeueReusableCellWithIdentifier("cellContent", forIndexPath: indexPath) as UITableViewCell
cellContent.textLabel!.text = "Second test"
return cellContent
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return contentArray[section]
}
Thanks!
Joe.
For solving of you're issue.You should enabled multiple selection by :
tableView.allowsMultipleSelection = YES;
Than you should remember items you selected in first viewContoller. For convenience try to use:
var indexPathsForSelectedRows: [NSIndexPath]? { get }
And than send this information to the another viewController by segue.
Example:
let array = ["a", "b", "c", "d", "e"]
#IBOutlet var initialTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
initialTableView.allowsMultipleSelection = YES
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueA" {
if let destination = segue.destinationViewController as? Content {
if let selectedRows = initialTableView.indexPathsForSelectedRows {
//hear you have option to send items that are selected to another controller or send only indexPath.
}
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.array.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellA", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = array[indexPath.row]
return cell
}
You have to store the selected rows yourself in a list or array
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//check if self._selectedIndices (your array) contains indexPath.row
//if yes then remove it from array
cell.accessoryType = UITableViewCellAccessoryType.None // remove check mark if you want to use it
//if no then add it to array
cell.accessoryType = UITableViewCellAccessoryType.Checkmark // add check mark if you want to use it
}
then in your prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// use self._selectedIndices to get the selected row indices
// self._dataSource[index] for the actual data
}
Just enable multiple selection on the tableview, and use the indexPathsForSelectedRows variant to get the full list. You can then either pass the whole list from your data source + the indexes, or map the indexes to the actual values.

Complete In-Depth switch statement malfunction

I have asked this question once before however I feel like i haven't been as thorough as can be. I am attempting to complete a very standard drill down table view hierarchy programmatically rather than using the IB to avoid unnecessary scramble due to the fact i have well over 40 different views i want to implement. I have decided to use the following switch-statement:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var VC: UITableViewController
switch indexPath.row {
case 0: VC = SecondTableViewController()
default: ()
}
navigationController?.pushViewController(VC, animated: true)
}
as you can see it gives me the non-initialized error, so i then proceed to make my variable an optional to fix this issue & it compiles and runs:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var VC: UITableViewController?
switch indexPath.row {
case 0: VC = SecondTableViewController()
default: ()
}
navigationController?.pushViewController(VC!, animated: true)
}
however when i select the designated row (which is correct at the value of 0 after running under the debugger) it crashes with this error:
what seems to be the issue? is it the default statement within my switch? or is it the variable within my "pushViewController" method? Might i add, when i change the parameter within this method from "VC/VC!" to "UITableViewController()" like such:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var VC: UITableViewController?
switch indexPath.row {
case 0: VC = SecondTableViewController()
default: ()
}
navigationController?.pushViewController(UITableViewController, animated: true)
}
it runs & functions accordingly, but when the view is pushed, it is not to the TableViewController i designated in my switch statement, rather then just a blank table view. what am i missing?
HERE IS THE CODE FOR MY SecondTableViewController :
import UIKit
class SecondTableViewController: UITableViewController {
var myVariable = ["LIST OF STRINGS IN AN ARRAY"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myVariable.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! UITableViewCell
var superVariable = myVariable [indexPath.row]
cell.textLabel!.text = superVariable
return cell
}
}
The problem lies in SecondTableViewController where you didn't define an identifier for your cell. You should do something like this,
class SecondTableViewController: UITableViewController {
let theData = ["one", "two", "three", "four"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return theData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = theData[indexPath.row]
return cell
}
}

Resources