Complete In-Depth switch statement malfunction - ios

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
}
}

Related

Get indexPath for cell of UITable embedded in UITableViewCell

So here is the structure of the TableView:
There is a main UITableView, and inside each UITableViewCell there is another UITableview
Screenshot:
Each of the UITableViewCells have been designed as Custom Views and have been added by loading it from the Nib in the cellForRowAtIndexPath function.
What I want to do is for any option selected in the inner Table Views I want to get the index path of the cell that the Table View is embeded in.
Document Layout:
I tried to follow the delegate approach mentioned by Paulw11 here:
swift: how to get the indexpath.row when a button in a cell is tapped?: StackOverflow
Note: The method suggested by Paulw11 works perfectly
Code(TableVC):
class TableVC: UITableViewController, QuestionCellDelegate {
override func viewDidLoad() {
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()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 5
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 220.0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("QuestionCell", owner: self, options: nil)?.first as! QuestionCell
cell.delegate = self
return cell
}
func sendCellInfo(cell: UITableViewCell) {
print(cell)
let indexPathForQuestion = tableView.indexPath(for: cell)
print(indexPathForQuestion)
}}
Code(QuestionCell):
protocol QuestionCellDelegate: class {
func sendCellInfo(cell: UITableViewCell)
}
class QuestionCell: UITableViewCell, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var optionsTableview: UITableView!
var delegate: QuestionCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.optionsTableview.delegate = self
self.optionsTableview.dataSource = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("OptionsCell", owner: self, options: nil)?.first as! OptionsCell
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = optionsTableview.cellForRow(at: indexPath)
print("selectedCell")
self.delegate?.sendCellInfo(cell: selectedCell!)
}}
Code(OptionsCell):
class OptionsCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}}
Note: The current O/P is nil
Note: Code changed as per commented by pbasdf, realised the mistake
Found the solution due to pbasdf comment:
Delegate Function in TableVC:
func sendCellInfo(cell: UITableViewCell) {
/*** Take the cell passed and convert to a CGPoint wrt to TableView ***/
let cellPosition: CGPoint = cell.convert(cell.bounds.origin, to: self.tableView)
/*** wrt to CGPoint find index on current TableView ***/
/*** Returns as Section,Cell ***/
let indexPathForSelectedCell = (tableView.indexPathForRow(at: cellPosition)?.row)
print(indexPathForSelectedCell)
}
The following answer is added #Supratik Majumdar request for the logic which I said.
Supratik try using the following code, I hope you will get your need done.
//Initialize your question or answer in viewDidLoad or where ever you like to as shown below
self.questionArray = ["Question1", "Question2"]
self.optionArray = [["Option 1", "Option 2", "Option 3", "Option 4"], ["Option 1", "Option 2", "Option 3", "Option 4"]]
//Make us of the following tableview delegate & datasource code
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.questionArray.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.OptionArray[section].count
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
return self.questionArray[section]
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
let currentOptionArray = self.questionArray[section]
let currentOption = currentOptionArray[indexPath.row]
cell.textLabel.text = currentOption
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedIndex = indexPath
let selectedQuestionIndex = indexPath.section
let selectedOptionIndex = indexPath.row
//Make use of the data you need in the above values
}
Use this:
tableView.cellForRowAtIndexPath(YourIndexPath) as! OptionCell
You can do your own indexPath as global variable and filing it on didSelectRow method
YourIndexPath = indexPath

Swift- custom UITableViewCell delegate to UIViewController only one protocol works

In the application, I have custom protocols that my UIViewController conforms to. I have a custom tableViewCell class and have UIImageView and UITextView in there. I set the cell's delegate to the UIViewController after dequeuing. However only one of the custom protocols makes the callback (imagepicker protocol).
protocol customProtocol1{
func pickImage(myInt: Int)
}
protocol customProtocol2{
func protocol2 (myInt: Int)
}
class controller1: UIViewController, UITableViewDelegate, customProtocol1, customProtocol2 {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section:Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as! CustomTableCellClass
cell.delegate = self
return cell
}
func pickImage ( myInt: Int){
print("This line prints")
}
func protocol2 (myInt: Int){
print ("This line doesn't print")
}
}
And here's the customTableCellClass code:
class CustomTableCellClass: UITableViewCell, UITextFieldDelegate, UITextViewDelegate {
var imageDelegate: customProtocol1?
#IBAction func pickImage( sender: AnyObject) {
imageDelagate?.pickImage(205)
}
var somethingElseDelegate: customProcotol2?
#IBActon func clickOnButton( sender: AnyObject) {
print("this line prints")
somethingElseDelegate?.protocol2(2)
}
override func awakeFromNib(){
super.awakeFromNib()
}
}
My question is, why does the first protocol get callbacks but second does not?
From what I see in your code, you only set one delegate, change your code in cellForRowAtIndexPath to
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as! CustomTableCellClass
cell.imageDelegate = self
cell.somethingElseDelegate = self
return cell
}
Your custom cell has two delegate properties, imageDelegate and somethingElseDelegate, but in your implementation of tableView(tableView:cellForRowAtIndexPath:) you only assign one property.
If you set both properties your implementation should work.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeReusableCellWithIdentifier("customCell", forIndexPath: indexPath) as! CustomTableCellClass
cell.imageDelegate = self
cell.somethingElseDelegate = self
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!

hidesBarsOnSwipe will show toolbar unexpectedly

I set hidesBarsOnSwipe=true in two view controllers, the Foo view controller which does not have a toolbar pushes Bar view controller which does have a toolbar. When Bar pop back to Foo, the toolbar will show when I scroll the Foo view controller. If I don't set self.navigationController?.toolbarHidden = false in Bar view controller, the toolbar will not show in neither Bar nor Foo view controller. I work with iOS8 SDK + Xcode 7.1.1 + Swift 2.1.
class FooTableViewController : UITableViewController
{
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.toolbarHidden = true
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.toolbarHidden = true
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "12345678"
return cell
} }
class BarTableViewController : UITableViewController
{
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.hidesBarsOnSwipe = true
self.navigationController?.toolbarHidden = false
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.hidesBottomBarWhenPushed = false
self.navigationController?.toolbarHidden = true
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "12345678"
return cell
}}
You are up against 2 problems:
You cannot add anything to a UITableViewController
All the programmatic toolbarHidden and hidesBarsOnSwipe is only confusing the OS,and you will end up with toolbars on the wrong layer, scrolling along with the table view, and similar oddities with the navigation bar.
What you want is for the second table view to be handled by a UIViewController, not a UITableViewController. There is little programmatic change, you only need your UIViewController to adopt the data source and table delegate protocols.
No need to tinker with showing/hiding the toolbar. The code is much simpler, you can define everything not listed below in the Storyboard.
Foo (no toolbar):
class FooTableViewController: UITableViewController {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Foo \(indexPath.row)"
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("Bar", sender: self)
}
}
Bar (with toolbar):
class BarTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Bar \(indexPath.row)"
return cell
}
}
Final Product:
► Find this solution on GitHub and additional details on Swift Recipes.

Error when trying to reload data in UITableView in Swift

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 :)

Resources