cellForRowAtIndexPath not called after reloadData but numberOfRowsInSection does - ios

In my current project I'm trying to fetch news from a REST-Endpoint and I wanna refresh the UITableView once the network request is finished. So I call reloadData() but unfortunately cellForRowAtIndexPath isn't called however numberOfRowsInSection gets called and returns the correct number of items.
Following my code stripped down to the relevant parts:
class NewsTableViewController: UIViewController {
private var newsItems: Array<NewsItem>!
private var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
newsItems = []
addDummyItem()
prepareTableView()
getNews()
}
func addDummyItem(){
let newsItem = NewsItem()
newsItem.dateString = "12th May"
newsItem.title = "Lorem ipsum"
newsItem.imgUrl = "URL"
newsItem.url = "URL"
newsItem.message = "Lorem ipsum dolor sit amet"
self.newsItems.append(newsItem)
}
func getNews(){
let url = NSURL(string: "http://someurl.com/news")
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: url!)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) { data, response, error in
guard data != nil && error == nil else {
print(error)
return
}
guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else {
print("status code not 200; \(response)")
return
}
let json = String(data:data!, encoding: NSISOLatin1StringEncoding)
let arr: AnyObject? = json?.parseJSONString
for item in arr as! Array<AnyObject>{
let newsItem = NewsItem()
newsItem.dateString = (item["dateString"] as AnyObject? as? String) ?? ""
newsItem.title = (item["title"] as AnyObject? as? String) ?? ""
newsItem.imgUrl = (item["imgUrl"] as AnyObject? as? String) ?? ""
newsItem.url = (item["url"] as AnyObject? as? String) ?? ""
newsItem.message = (item["message"] as AnyObject? as? String) ?? ""
self.newsItems.append(newsItem)
}
print("Network request finished - call reloadData()")
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
task.resume()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
automaticallyAdjustsScrollViewInsets = false
}
private func prepareTableView() {
tableView = UITableView()
tableView.dataSource = self
tableView.delegate = self
tableView.separatorColor = UIColor.clearColor()
}
}
extension NewsTableViewController : UITableViewDataSource{
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("Item count: ", newsItems.count)
return newsItems.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("cellForRowAtIndexPath called with indexPathRow: ", indexPath.row)
let cell = tableView.dequeueReusableCellWithIdentifier("ImageCardViewCell") as! NewsImageCardViewCell
let item: NewsItem = newsItems[indexPath.row]
getImageCardView(item.url, btnDate: item.dateString, textTitle: item.title, textDetail: item.message, imageCardView: cell.imageCardView, imageName: "news_1", indexPath: indexPath)
return cell
}
}
extension NewsTableViewController : UITableViewDelegate{
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
As you can see: The UITableView is correctly setup by setting the dataSource and delegate to my class. The reloadData() method is called on the main thread. Following the log-output:
Item count: 1
cellForRowAtIndexPath called with indexPathRow: 0
Network request finished - call reloadData()
Item count: 72
Note: At first there's one dummy item my array just to show you that cellForRowAtIndexPath is indeed called but only one time. After triggering reloadData() on numberOfRowsInSection is called but not the cellForRowAtIndexPath method.
I know that there're a lot of similar issues like this and I've checked them all for possible solutions but none were working. I appreciate any help.
Edit
Please note that the dummy view which is added before the network request is visible. So the UITableViews' height is not 0.
Edit #2
Thanks to everyone for your answers. I've found the issue. I've instantiated the ViewController via the Storyboard but I missed to set an IBOutlet. Due that the bounds of my UITableView were 0,0,0,0.

That because your table view did not add to your window, If the table view is not visible, the UIKit will not deal with UI related methods. It's kind of Apple's lazy load.
Change your prepareTableView function as below:
private func prepareTableView() {
tableView = UITableView(frame: self.view.bounds, style: .Plain)
tableView.dataSource = self
tableView.delegate = self
tableView.separatorColor = UIColor.clearColor()
self.view.addSubview(tableView)
}

Check Following :
1 numberOfRowsInSection see the Number of return value
2 if step1 is called and return right value still not calling cellForRowAtIndexPath that means your table view height is less then height of cell or table view height is 0 , print tableview bounds and also check superview of tableview has clips to bounds to TRUE

tableview datasource method cellForRowAtIndexPath is depending on numberOfRowsInSection . So it may be possiblity that you having zero item in newsItems

Related

Data from firebase not populating in my table view

So I recently asked a question regarding firebase - but have refactored my code quite a bit as I wanted all of my firebase methods to be done in the same place. I am having the following issue after the refactor...
Data from my firebase database is not populating my tableview. I'm not too sure why this would be, as it was working fine before I moved the method to a separate file from my table view(for cleaner code). All I did was move the method that populates the array to a separate file, return an array and then reload the tableview after calling the method. Below is the code in question:
In my FireBaseMethods class
//-------------- POPULATE TABLE ARRAY -----------------//
public func populateConsumableTableArray() -> [Consumable]{
var tableArray = [Consumable]()
//let the object populate itself.
ref.child("Consumables").observe(.childAdded, with: { snapshot in
let dataChange = snapshot.value as? [String:AnyObject]
let aRequest = Consumable(aDict: dataChange!)
tableArray.append(aRequest)
})
return tableArray
}
In my ListOfConsumablesViewController table view class
class ListOfConsumablesViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
private var methods:MethodsForController = MethodsForController()
private var fireBaseMethods:FireBaseMethods = FireBaseMethods()
private var consumableArray = [Consumable]()
let picker = UIImagePickerController()
#IBOutlet weak var consumableTable: UITableView!
//-------------------- VIEW DID LOAD -----------------------//
override func viewDidLoad() {
super.viewDidLoad()
//Trying to populate the table view here...
consumableArray = fireBaseMethods.populateConsumableTableArray()
consumableTable.reloadData()
self.consumableTable.dataSource = self
self.consumableTable.delegate = self
}
...
//---------------------- FUNCTIONS FOR TABLE VIEW CELLS & TABLE ----------------------//
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(consumableArray.count)
return consumableArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "consumableCell", for: indexPath) as! ConsumablesCell
cell.layer.borderWidth = 1
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.backgroundColor = UIColor.white
cell.adapterType.text = consumableArray[indexPath.row].getType()
cell.count.text = String(consumableArray[indexPath.row].getCount())
if Int(consumableArray[indexPath.row].getCount()) ?? 0 <= 0{
cell.count.textColor = UIColor.red
}else{
cell.count.textColor = UIColor.black
}
cell.sku.text = consumableArray[indexPath.row].getSku()
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}
}
As shown below, nothing populates in the table view... I'm assuming it's something to do with the method being in a separate file, but I'm not really sure why that would be?
Simple implementation of a completion handler
//-------------- POPULATE TABLE ARRAY -----------------//
public func populateConsumableTableArray(completion: #escaping (Consumable) -> Void) {
//let the object populate itself.
ref.child("Consumables").observe(.childAdded, with: { snapshot in
guard let dataChange = snapshot.value as? [String:AnyObject] else { return }
let aRequest = Consumable(aDict: dataChange)
completion(aRequest)
})
}
override func viewDidLoad() {
super.viewDidLoad()
self.consumableTable.dataSource = self
self.consumableTable.delegate = self
//Trying to populate the table view here...
fireBaseMethods.populateConsumableTableArray { [unowned self] consumable in
self.tableArray.append(consumable)
DispatchQueue.main.async {
self.consumableTable.reloadData()
}
}
}

Error setting UILabel text in custom UITableViewCell

I am quite new to Swift programming, but I am having trouble setting UILabel text in my UITableView class for individual UITableViewCell instances.
I have created a custom subclass of UITableViewCell called PizzaTableViewCell and a custom UITableView class called PizzaListTableViewController. I am trying to populate the UITableView instance with data from an array, which is being populated from an API call to my node.js server.
I have included my UITableView subclass, custom UITablveViewCell class, the struct for the data, and a link to a screenshot of the Simulator loading what I have done. Any help is greatly appreciated!
I have verified that the data is being put in the array with no issues, as I can print the contents after the call to fetchInventory method. I have been able to set a single textLabel with
cell.textLabel?.text = pizzas[indexPath.row].name
along with an image in the array with:
cell.imageView?.image = pizzas[indexPath.row].image
but I have 2 more labels that I need in each cell which I cannot set. I have checked my IBOutlets and Storyboard identifiers, and they match the code.
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
print(self.pizzas)
//self.tableView.reloadData()
//print(self.pizzas)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
//print(data)
//print("CHECK")
print("Printing each item: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
completion(inventory)
}
}
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("ROWS", pizzas.count)
return self.pizzas.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
//cell.pizzaImageView?.image = pizzas[indexPath.row].image
//THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS
//CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY
cell.imageView?.image = pizzas[indexPath.row].image
cell.textLabel?.text = pizzas[indexPath.row].name
//cell.textLabel?.text = pizzas[indexPath.row].description
//cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)"
// cell.name?.text = pizzas[indexPath.row].name
// cell.imageView?.image = pizzas[indexPath.row].image
// cell.amount?.text = "$\(pizzas[indexPath.row].amount)"
// cell.miscellaneousText?.text = pizzas[indexPath.row].description
//print(cell.name?.text! as Any)
print(cell.imageView as Any)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizzaSegue" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}
class PizzaListTableViewController: UITableViewController {
var pizzas: [Pizza] = []
override func viewDidLoad() {
super.viewDidLoad()
//title you will see on the app screen at the top of the table view
navigationItem.title = "Drink Selection"
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
//tableView.estimatedRowHeight = 134
//tableView.rowHeight = UITableViewAutomaticDimension
fetchInventory { pizzas in
guard pizzas != nil else { return }
self.pizzas = pizzas!
print(self.pizzas)
//self.tableView.reloadData()
//print(self.pizzas)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
} //end of viewDidLoad
private func fetchInventory(completion: #escaping ([Pizza]?) -> Void) {
Alamofire.request("http://127.0.0.1:4000/inventory", method: .get)
.validate()
.responseJSON { response in
guard response.result.isSuccess else { return completion(nil) }
guard let rawInventory = response.result.value as? [[String: Any]?] else { return completion(nil) }
let inventory = rawInventory.compactMap { pizzaDict -> Pizza? in
var data = pizzaDict!
data["image"] = UIImage(named: pizzaDict!["image"] as! String)
//print(data)
//print("CHECK")
print("Printing each item: ", Pizza(data: data))
//printing all inventory successful
return Pizza(data: data)
}
completion(inventory)
}
}
#IBAction func ordersButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "orders", sender: nil)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//PRINTING ROWS 0 TWICE in console
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("ROWS", pizzas.count)
return self.pizzas.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: PizzaTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Pizza", for: indexPath) as! PizzaTableViewCell
//cell.backgroundColor = Services.baseColor
//cell.pizzaImageView?.image = pizzas[indexPath.row].image
//THESE WORK BUT ARE A STATIC WAY OF SETTING THE CELLS
//CAN ONLY SET THE SELL WITH A SINGLE TEXT LABEL FROM THE DATA ARRAY
cell.imageView?.image = pizzas[indexPath.row].image
cell.textLabel?.text = pizzas[indexPath.row].name
//cell.textLabel?.text = pizzas[indexPath.row].description
//cell.textLabel?.text = "$\(pizzas[indexPath.row].amount)"
// cell.name?.text = pizzas[indexPath.row].name
// cell.imageView?.image = pizzas[indexPath.row].image
// cell.amount?.text = "$\(pizzas[indexPath.row].amount)"
// cell.miscellaneousText?.text = pizzas[indexPath.row].description
//print(cell.name?.text! as Any)
print(cell.imageView as Any)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100.0
} //END OF
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "pizzaSegue", sender: self.pizzas[indexPath.row] as Pizza)
} //END OF override func tableView
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "pizzaSegue" {
guard let vc = segue.destination as? PizzaViewController else { return }
vc.pizza = sender as? Pizza
}
} //END OF override preppare func
}
struct Pizza {
let id: String
let name: String
let description: String
let amount: Float
//let amount: String
let image: UIImage
init(data: [String: Any]) {
//print("CHECK:: pizza.swift")
self.id = data["id"] as! String
self.name = data["name"] as! String
// self.amount = data["amount"] as! Float
self.amount = ((data["amount"] as? NSNumber)?.floatValue)!
self.description = data["description"] as! String
self.image = data["image"] as! UIImage
}
}
As noted above, I have been able to print the contents of the data array with beer names, pictures, descriptions and etc. I have tried to print to console
print(cell.name?.text)
after setting
cell.name?.text = pizzas[indexPath.row].name
but it prints nil and this is a problem. I have been stuck with this for about 2 weeks!
IBOutlets screenshot:
I think i found your Problem, let me explain
What you are doing here is you have a custom UITableViewCell defined in the Storyboard in a Controller named "Root View Controller" which is not your PizzaListTableViewController to put it simply
And as you said you have absolutely no issue regarding the IBOutlets
Now when you say
tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza")
In Your PizzaListTableViewController you are not linking it with the UI of the cell rather just the Code (This is only used when there is no xib of the cell)
Now what you can do to solve this
Solution # 1
Move/Copy your UI of the PizzaTableViewCell to PizzaListTableViewController in the storyboard from your "Root View Controller"
Make sure you add a Reuse Identifier in the Attribute Inspector of the cell in the storyboard
remove tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") this wont give you an error this time as it will automatically get register
Make sure all the IBOutlets are connected
Solution # 2
create a separate Nib (xib) of the cell
and now you have to register the cell here like
tableView.register(UINib(nibName: "PizzaTableViewCell", bundle: Bundle.main), forCellReuseIdentifier: "PizzaCell")
Hope this helps.
Try this
cell.name?.text = ...
cell.amount?.text = ...
cell.miscellaneousText?.text = ...
cell.pizzaImageView?.image = ...
If it still does not work then make sure your cell and your outlets are not null when setting its value. Hope it helps !
There is something definitely strange going on with your setup.
If you try to name the IBOutlets with the same name as the UITableViewCell default property it'll throw an error. The fact that you were able to set those names and build successfully is strange.
From the screenshot above you can see what happens when I attempted to do this.
Make sure your Table View Controller class is set in the storyboard.
Make sure your Table View Cell class is set in the storyboard.
Make sure that all your outlets are properly connected.
Make sure your Table View Cell Identifier is provided in the storyboard.
My Table View Controller Subclass
My Table View Cell Subclass
cell.imageView?.image and cell.textLabel?.text are optional properties of the table view itself. They are not the properties of the custom cell that you designed.
You use tableView.register(PizzaTableViewCell.self, forCellReuseIdentifier: "Pizza") when you have designed a table view cell in XIB. But as you have designed the cell in the storyboard itself you should set the cell reuse identifier and cell class in the storyboard.
I hope this will help you out.

empty tableview cell for at index path row is not called

All the tableview functions are working except cell for row index path .
The problem maybe that foods array is empty so the number for rows is 0 so the cell for row at index path is not called
#IBOutlet weak var foooods: UITableView!
var databaseref = Database.database().reference()
var img : AnyObject?
var foods = [String?]()
override func viewDidLoad() {
super.viewDidLoad()
self.databaseref.child("basic food").observe(.childAdded, with: {( snap: DataSnapshot) in
let snapp = snap.value as! [String:AnyObject]
if let x = snapp["name"] as! String? {
self.foods.insert(x, at: 0)
//self.foods.append(x)
}
})
self.foooods.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.foods.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("difufuehf")
let cell : foodsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "aupa", for:indexPath) as! foodsTableViewCell
print("fufvksdfvysdgfvjdsgfdsygfvds,jhvjsdvsdjvguydsfgdsylfgdsyfgsdlygfsiygf")
if let foo = foods[indexPath.row] {
print(foo)
cell.food.text = foo
}
return cell
}
This must be a duplicate but I can't find one.
Your issue is that you call reloadData in the wrong place which results in it being called far too soon. You need to call it inside the completion block, after you update your data model.
And you need to make sure it gets called on the main queue.
override func viewDidLoad() {
super.viewDidLoad()
self.databaseref.child("basic food").observe(.childAdded, with: {( snap: DataSnapshot) in
if let snapp = snap.value as? [String:Any], let x = snapp["name"] as? String {
self.foods.insert(x, at: 0)
//self.foods.append(x)
DispatchQueue.main.async {
self.foooods.reloadData()
}
}
})
}
Note that I also fixed the way the value is obtained. You really need to avoid force unwrapping and force casting.

NSUserDefault synchronize on back button swift

I have a problem with my UITableView. I have 3 views with navigation controller. The second one have a table, the third is a search with some values. I want to click on button search (third view) and open second view and update the table. But not update. To work I have to return to first view and open again second view.
Second view code:
class Empresa: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate{
let textCellIdentifier = "cell"
var menu:[[String]] = [[]]
var buscaEmp:BuscadorEmpresa = BuscadorEmpresa()
#IBOutlet weak var tablaVista: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
recuperaEmpresas()
tablaVista.delegate = self
tablaVista.dataSource = self
}
func recuperaEmpresas(){
menu = buscaEmp.getEmpresas()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menu.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! CustomTableViewCell
let row = indexPath.row
cell.nombreEmp.text = menu[row][0]
return cell
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
recuperaEmpresas()
self.tablaVista.reloadData()
}
As you can see I reload my tableView (tablaVista) on viewWillAppear but don't update.
RecuperaEmpresa() call this method:
func getEmpresas() -> [[String]]{
NSUserDefaults.standardUserDefaults().synchronize()
var array = [[""]]
if((NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")) != nil){
array = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]]
return array
}
return array }
I was debugging and it's like getEmpresa don't update first time
EDIT:
GetEmpresa not return the last value the first time, but if I print return correct value. The problem must be NSUSERDEFAULT
EDIT WITH IMAGES
EDITED FOR Alessandro Ornano
Finally my tableView reload but I see an instant my last values and change it with news. I use that:
Second view:
let textCellIdentifier = "cell"
var menu:[[String]] = [[]]
//var buscaEmp:BuscadorEmpresa = BuscadorEmpresa()
#IBOutlet weak var tablaVista: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
recuperaEmpresas()
tablaVista.delegate = self
tablaVista.dataSource = self
}
func recuperaEmpresas(){
self.menu = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]]
//self.menu = buscaEmp.getEmpresas()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menu.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(textCellIdentifier, forIndexPath: indexPath) as! CustomTableViewCell
let row = indexPath.row
cell.nombreEmp.text = menu[row][0]
return cell
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
recuperaEmpresas()
viewDidLoad()
self.tablaVista.reloadData()
}
Third view button call this methods:
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil
{
print("error=\(error)")
return
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
var err: NSError?
do{
let myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
let jsonEmpresas = myJSON["TablaEmp"]!!["item"]
let empresas: [[String: AnyObject]]
if let multipleEmpresas = jsonEmpresas as? [[String: AnyObject]] {
empresas = multipleEmpresas
} else if let singleEmpresa = jsonEmpresas as? [String: AnyObject] {
empresas = [singleEmpresa]
} else {
empresas = []
}
for empresa in empresas{
let zcif = empresa["Zcif"] as? String
let zcccp = empresa["Zcccp"] as? String
let zfax = empresa["Zfax"] as? String
let zdocu = empresa["Zdocu"] as? String
self.arrayEmpresas.append([zcif!, zcccp!, zfax!, zdocu!])
}
NSUserDefaults.standardUserDefaults().setObject(self.arrayEmpresas, forKey:"ARRAYEMPRESA")
NSUserDefaults.standardUserDefaults().synchronize()
}catch { print(error)}
}
task.resume()
And
func back(){
navigationController?.popViewControllerAnimated(true)
}
getMethod:
func getEmpresas() -> [[String]]{
NSUserDefaults.standardUserDefaults().synchronize()
var array = [[""]]
if((NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")) != nil){
array = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]]
return array
}
return array
}
When you have this kind of issue many times there are involved synchronism between sampling data and UI update. In your code I dont know where you get this data, how long it takes to arrive and how you set this array on NSUserDefaults. So, if you can't see immediatly your UI updates probably you launch reloadData (main thread) too early because there are still operations to the background thread (http server queries, data fetching...)
You can also write a more elegant:
func getEmpresas() -> [String] {
NSUserDefaults.standardUserDefaults().synchronize()
if let array = NSUserDefaults.standardUserDefaults().arrayForKey("ARRAYEMPRESA")! as! [[String]] {
return array
}
return [String]()
}
UPDATE:
I've seen your update code. I want to focus your attention in this method:
func recuperaEmpresas(){
// Are my data ready to read??? who can say to me this in main thread??
menu = buscaEmp.getEmpresas()
}
So you cant call self.tablaVista.reloadData() until you are pretty sure your data can be exists. To do it you can use a completion handler (click me if you want to know how to build it)
func recuperaEmpresas(url: NSURL,completionHandler: CompletionHandler){
callMyNetwork() { //do call and after finish do...
menu = buscaEmp.getEmpresas()
self.tablaVista.reloadData()
}
}
Try to put tableVista.reloadData() in your prepareForSegue or unwindToHome function (whichever you are using to go back to second view). If this is not possible, you can always set a bool var which determines if the view is loaded from the first view or the second view and then reload data accordingly.
Hope this helps. :)

How to retrieve continuous data from mysql

I'm trying to find tutorials or examples on how to load a tableView or collectionView for continuous scrolling.
For example, I have a database of over 1000 entries but I only want to fetch a 100 at a time. Then when the scroll reaches the bottom it automatically loads the next 100, and so on...
Does anyone know of any tutorials or examples?
I'm coding using swift 2 but any objective-C examples would be fine as I can usually figure out the translation.
I have created a test app to try it out. It fetches 20 records starting from zero.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var personTable: UITableView!
var people = []
var startIndex:Int = 0;
override func viewDidLoad() {
super.viewDidLoad()
self.personTable.delegate = self
self.personTable.dataSource = self
fetchData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.people.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("personCell") as! TableViewCell
cell.usernameLabel.text = people[indexPath.row]["username"] as? String
return cell
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == people.count - 1{
self.startIndex += 20
fetchData()
}
}
func fetchData(){
let post = PostService()
let data = ["startIndex":String(self.startIndex),"limit":"20"]
let url = "http://**********/index.php?action=fetchPersonList"
post.post(data, url: url) { (succeeded, msg) -> () in
if succeeded{
if msg as! String != "<null>"{
self.people = msg as! NSArray
self.personTable.reloadData()
}
}
}
}
}
it works when the last cell is reached and reloads the data but doesn't stop until all the rows have been downloaded which is when it throws an error as there is no more data.
I'm not sure where to go from here.
I've looked for the last couple of hours online to try and find examples but either i'm not using the right search terms or there aren't that many out there.
After several hours of experimenting with this (that I was hoping to avoid by finding a tutorial which wasn't to be.) I eventually found a method that works.
This is the code that works for me.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var personTable: UITableView!
var people = [NSDictionary]()
var page = 0
var holdPage = false;
override func viewDidLoad() {
super.viewDidLoad()
self.personTable.delegate = self
self.personTable.dataSource = self
fetchData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.people.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("personCell") as! TableViewCell
cell.usernameLabel.text = people[indexPath.row]["username"] as? String
return cell
}
func scrollViewDidScroll(scrollView: UIScrollView) {
let actualPosition:CGFloat = scrollView.contentOffset.y;
let contentHeight:CGFloat = scrollView.contentSize.height
let viewHeight:CGFloat = scrollView.frame.height
if (actualPosition + viewHeight >= contentHeight && self.holdPage == false && self.page < 3) {
self.holdPage = true //this is used to stop this if statement from running more than once.
self.page++
self.fetchData()
}
}
func fetchData(){
let post = PostService()
let startIndex = self.page * 20
let data = ["startIndex":String(startIndex),"limit":"20"]
let url = "http://*********/index.php?action=fetchPersonList"
post.post(data, url: url) { (succeeded, msg) -> () in
if succeeded{
if let data = msg as? [NSDictionary]{
self.people = self.people + data
self.holdPage = false
self.personTable.reloadData()
}
}
}
}
}
I had to use the holdPage variable as I found that when the scroll reached the end it loaded several pages in one go.
The only drawback to this method is that it keeps adding rows to the people array as you scroll down making it bigger each time. I was initially looking to remove the rows that were no longer in view but that was proving to be very complex.
Unless someone can show me a good example?

Resources