UITableView numberOfRows - ios

I want to update a tableview with server data.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var aedList = [AED]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.aedList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
Server().getJsonInformationFromServer(url: "aeds") { (response) -> Void in
self.aedList = JSONHelper().parseAEDInformation(json: response["data"])
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cell.textLabel?.text = self.aedList[indexPath.row].street
self.tableView.reloadData()
})
}
return cell
}
The data is being downloaded correctly, but the tableview is empty.
I guess this is due to the async behaviour of my download and the numberOfRowsmethod tries to count an still empty array.
How do I correct this to display the data correctly? Do I need to use a dispatch_async there as well?

cellForRowAtIndexPath as the method name implies, its called by the table view for each cell so you should not fetch your entire data there, its for populating the cells individually. Here are a couple of fixes in your code:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var aedList = [AED]()
//In addition to viewDidLoad, you can call this method whenever you wanna update your table
func fetchData() {
Server().getJsonInformationFromServer(url: "aeds") { (response) -> Void in
self.aedList = JSONHelper().parseAEDInformation(json: response["data"])
self.aedList = JSONHelper().parseAEDInformation(json: response["data"])
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.tableView.reloadData()
}
})
}
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return aedList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
cell.textLabel?.text = self.aedList[indexPath.row].street
return cell
}

Loading data from tableView:cellForRowAtIndexPath: won't never work. This method is called for every cell, that means:
If there are no cells (data is not loaded yet), the method won't never be called and your data won't be loaded (which is what is happening now).
If the data were loaded, you would trigger a new loading for every displayed cell, that is, if you have 5 items, you trigger 5 loadings. And that's recursive because reloadData will redisplay all cells.
Solution:
Put your loading code to a better place, e.g. viewDidAppear method of your controller.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
Server().getJsonInformationFromServer(url: "aeds") { (response) -> Void in
self.aedList = JSONHelper().parseAEDInformation(json: response["data"])
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
cell.textLabel?.text = self.aedList[indexPath.row].street
return cell
}

Related

Creating tableView inside tableView

I've created a tableView with prototype cells. Inside each of these prototype cells is another tableView with different prototype cells. I've linked this all together fine, but I'm having trouble modifying the innermost prototype cells. Here is why.
Here is the relevant code:
class ViewController: UIViewController, AVAudioRecorderDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "outerCell") as! outerCell
//would obviously make some modification to cell here, like cell.title = "test" or something
let cell2 = cell.commentTableView.dequeueReusableCell(withIdentifier: "innerCell") as! innerCell
cell2.commentText.text = "sus"
//NEED TO DIFFERENTIATE HERE ON HOW TO KNOW WHICH CELL TO RETURN
//e.g. NEED TO RETURN either cell1 or cell2, depending on the tableView
}
My code for outerCell looks like this:
import UIKit
class outerCell: UITableViewCell, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var commentTableView: UITableView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
commentTableView.delegate = self
commentTableView.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 = tableView.dequeueReusableCell(withIdentifier: "innerCell", for: indexPath) as! commentCell
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
}
See, the main problem is, both these table views work fine and all, but, in the first chunk of code, if I just do something like,
if tableView == self.tableView{
return cell }
else ...
this won't work, as tableView always seems to be self.tableView.
How can I modify my code so that I can actually impact the text displayed in the inner cell, and the outer cell, in the same block of code?
Also, please note, I know that, based on the example given here, there is no need for these nested cells. I've just simplified the code here to focus on what's important - my actual code has a lot of stuff happening in both the inner and outer cell.
Thank you, any help would be appreciated.
you need to first create two different cell classes.
In outer class :
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SearchPreferredJobTableViewCell
cell.responseCreateBookingObj = { [unowned self] (returnObject) in
DispatchQueue.main.async {
tableView.beginUpdates()
}
// do your logic
DispatchQueue.main.async {
cell.contentView.layoutIfNeeded()
tableView.endUpdates()
} }
return cell
}
// other cell class
Declare variable
var responseCreateBookingObj : APIServiceSuccessCallback?
// send callback from you want to send
guard let callBack = self.responseCreateBookingObj else{
return
}
callBack(true as AnyObject)
// also do in when user scroll it'll manage
tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath){
DispatchQueue.main.async {
tableView.beginUpdates()
}
// do your logic
DispatchQueue.main.async {
cell.contentView.layoutIfNeeded()
tableView.endUpdates()
}
}

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
}

TableView cellForRowAtIndexPath is never called (iOS, Swift)

I have a problem with creating a table view in UITableViewController. I have tried everything I have found, reuseIdentifier is set correctly, number of rows returns non-zero number and height is set in storyboard. Only the cellForRowAtIndexPath is not called, other table functions runs correctly.
Here is the code:
TableViewController:
import UIKit
import Photos
class CropTableViewController: UITableViewController {
var photos = [Photo]()
var photoAssets = [PHAsset]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return photos.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! CropTableViewCell
let photo = photos[indexPath.row]
cell.photoImageView.image = photo.photo
return cell
}
}
TableViewCell:
import UIKit
class CropTableViewCell: UITableViewCell {
#IBOutlet weak var photoImageView: UIImageView!
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
}
}
The photos array is empty, so photos.count is zero, so numberOfRowsInSection returns 0, so the runtime knows that there are no cells and that there is no need to call cellForRowAtIndexPath.

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

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