Using UITableView with storyboard but separate datasource - ios

EDIT Answer below in this post
I'm trying to set up a UITableView controller in storyboard, with a separate datasource, and I've hit a wall. The data source doesn't seem to respond to changes or push it's 'updates' to the table view. I've tried implementing the data source in the MainMenuTableViewController which worked fine.
This is my MainMenuTableViewController
override func viewDidLoad() {
super.viewDidLoad()
sharedLightsManager.delegate = self
sharedLightsManager.loadNetworkContext()
dataSource = MainMenuTableViewDataSource(sharedLightsManager: sharedLightsManager)
tableView.dataSource = dataSource
tableView.delegate = dataSource
title = "test"
}
//This method fires each time a change happens
func updateLights(){
lights = sharedLightsManager.localNetworkContext.allLightsCollection.lights
tableView.reloadData()
}
MainMenuDataSource:
class MainMenuTableViewDataSource: NSObject, UITableViewDataSource, UITableViewDelegate
{
let reuseIdentifier = "tableViewCell"
var sharedLightsManager: SharedLightsManager?
var lights = []
init(sharedLightsManager: SharedLightsManager)
{
self.sharedLightsManager = sharedLightsManager
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lights.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("tableViewCell", forIndexPath: indexPath) as UITableViewCell
var lights = sharedLightsManager!.localNetworkContext.allLightsCollection.lights
var light = LFXLight()
if lights.count == 0 {
println("Lights array still loading...")
} else {
light = lights[indexPath.row] as LFXLight
}
return cell
}
}
and here is my outlets:
I've just figured it out. A bit embarrassing. It was due to the lights array not having any objects in it, so obv. lights.count would return 0, therefore no rows...

The data source will not push updates unless the UITableView is told to reloadData. If you change the numberOfRows value, it will not update unless the tableView is notified through methods like insertRowAtIndexPath, reloadData, deleteRowAtIndexPath etc.

Related

Why the cellForRowAt method is not getting called

-> I have taken a class which is a child of NSObject in that class I have created the tableView object.
-> The numberOfRowsInSection & cellForRowAt methods are in the same class
-> I have even assigned the object of the same class to the dataSource & delegate property
So my problem is the numberOfRowsInSection is getting called but the cellForRowAt is not getting called. I am just getting a tableView without any cell.
-> I have checked that the numberOfRowsInSection is not returning 0
Please Check The Code Below:
This is the class that I have created. I am creating the object of this class in a UIViewController child class
NOTE:-
1) I have called the configureTV() function in the init method.
In this configureTV() I have assigned both the dataSource & delegate property its value.
2) I have verified that the configureTV() method is getting called
3) I have verified that I am registering the reuseId for the cell
4) I have checked that the dataSource property is being assigned the reference or the object of the class successfully
5) I have verified that the numberOfRows & numberOfSection methods are being called. Even I have tried these two methods with hardcoded values as well
import UIKit
class MyClass:NSObject,UITableViewDataSource,UITableViewDelegate
{
var superView:UIView!
var tableView:UITableView = UITableView()
let value = ["A","B","C","D","E","F"]
override init()
{
super.init()
}
convenience init(superView:UIView)
{
self.init()
self.superView = superView
self.configureTV()
print("SuperView Initialized")
}
func configureTV()
{
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "c")
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.frame = self.superView.frame
self.superView.addSubview(self.tableView)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
print(value.count)
return value.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "c", for: indexPath)
cell.backgroundColor = .green
cell.textLabel?.text = value[indexPath.row]
return cell
}
}
//-------Creating The Object Of The Above Class------------
class ABCD:UIViewController
{
override func viewDidLoad()
{
let obj = MyClass(superView: self.view)
}
}
----------------------------I Got The Solution-------------------------
I found the solution by myself but the solution is very strange.
Inside the class ABCD I have created an object as Shown below
//Code Before Being Corrected: —
class ABCD:UIViewController
{
override func viewDidLoad()
{
let obj = MyClass(superView: self.view)
}
}
Now I have done a very minor change i.e. I have made the “obj” variable as the property of the class & the
Problem got solved.
//Code After Correction Which Solved The Problem:—
class ABCD:UIViewController
{
var obj:MyClass!
override func viewDidLoad()
{
obj = MyClass(superView: self.view)
}
}
I want to know why this happened.

How do I fill a UITableView that is a subview of a UIViewController?

I need to create a view that contains a vertical stack view, which holds a Label, a TableView, another Label, and a Button (in descending order). I have been struggling trying to configure the TableView, as I cannot get it to fill with cells (currently just appears as a blank space in the super view). Right now, I have a ViewController for the main view - 'YourOrderViewController' - and a TableViewController for the TableView - 'OrderItemsTableViewController'. It looks like this:
The main view
class YourOrderViewController: UIViewController{
var cellTitle = String()
var cellSubtitle = String()
#IBOutlet weak var orderListTable: UITableView!
let orderTableController = OrderItemsTableViewController()
override func viewDidLoad() {
super.viewDidLoad()
orderListTable.delegate = orderTableController
orderListTable.dataSource = orderTableController
}
And the TableView subview
class OrderItemsTableViewController: UITableViewController {
var drinkOrderList = [Drink]()
var foodOrderList = [Food]()
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if section == 0 {
return drinkOrderList.count + foodOrderList.count + 1
} else {
return 0
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "OrderItemCell", for: indexPath)
if indexPath.row < drinkOrderList.count {
cell.textLabel?.text = drinkOrderList[indexPath.row].drinkName
} else if indexPath.row - drinkOrderList.count < foodOrderList.count {
cell.textLabel?.text = foodOrderList[indexPath.row].foodName
} else {
print("Also here")
cell = tableView.dequeueReusableCell(withIdentifier: "AddToOrderCell", for: indexPath)
}
return cell
}
I initially tried making the whole thing in one view controller, a UIViewController that was the delegate and data source of the UITableView, but that did not work either. Any help is appreciated.
Plain & simple you are over doing it.
For example:
orderListTable.delegate = orderTableController
orderListTable.dataSource = orderTableController
orderTableController instance of OrderItemsTableViewController is a controller type, instead it should an NSObject type of class which conforms to tableView delegate & datasource.
class TableViewDataSource: NSObject, UITableViewDataSouce {
// no need of controller's life cycle
// just declare all & implement all protocol required
}
class TableViewDelegate: NSObject, UITableViewDelegate {
// have some property where you could set the data after you initialize this object
}
Now you could do
let _delegate = TableViewDelegate()
let _dataSource = TableViewDataSource()
_dataSource.someDataProperty = data //<-- important
orderListTable.delegate = _delegate
orderListTable.dataSource = _dataSource
Also, in you controller, you need to add method to reload this tableView
In your cellForRowAt, just use one custom cell for now until you got it working

swift how to get self.tableView.reloadTable() when UITableViewController is not class type

I have a normal view controller with a table view inside, so the class is just a normal UIViewController, therefor I am unable to call self.tableView with this.
I have an asynchronous call to create an array of Aircraft objects that is taken from an online database, but that is only completed after the table cells are initially loaded. I am trying to update the table cells after this asynchronous call is completed, but am unsure how to do so.
I do the async call in viewDidLaod(). Here is my current code that only displays the loading tags since the update has not taken place for the aircraft array.
class AircraftViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let log = Logger( id: String(AircraftViewController.self) )
let dvc = DownloadViewController()
var aircraftArr = [Aircraft]()
override func viewDidLoad() {
super.viewDidLoad()
dvc.getMobileSystemOverviewHTML {
dispatch_async(dispatch_get_main_queue()){
self.aircraftArr = self.dvc.aircraft
self.log.debug("\n\n\n\n\n \(self.aircraftArr) \n\n\n\n\n")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table View Delegate Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("AircraftCell", forIndexPath: indexPath) as! AircraftCell
if indexPath.row < aircraftArr.count{
let singleAircraft = aircraftArr[indexPath.row] as Aircraft
cell.aircraft = singleAircraft
} else {
cell.aircraft = Aircraft(tailID: "Loading", aircraftSN: "Loading", Box_SN: "Loading")
}
return cell
}
You need to create the outlet of tableview like this
#IBOutlet var tableView: UITableView!
After that reload this tableview in your dispatch_async
dvc.getMobileSystemOverviewHTML {
dispatch_async(dispatch_get_main_queue()){
self.aircraftArr = self.dvc.aircraft
self.tableView.reloadData()
self.log.debug("\n\n\n\n\n \(self.aircraftArr) \n\n\n\n\n")
}
}
Hope this will help.

Added a tableview on a viewController, but the data is not there

My goal is to make a grouped tableView, but for somehow the data is not added to the table View
Here's the story board picture
I added a table View on top of view controller which is
and the code that I wrote seems like it don't work
import UIKit
import Alamofire
import SwiftyJSON
import KeychainAccess
class SettingsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
let keychain = Keychain(server: "https://genietesting.herokuapp.com", protocolType: .HTTPS)
var profile: [String]?
let aboutGenie = [
"How it works",
"About",
"Contact"
]
override func viewDidLoad() {
super.viewDidLoad()
let firstName = keychain[string: "first_name"]
profile = [
firstName!
]
tableView.dataSource = self
tableView.delegate = self
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return profile!.count
} else {
return aboutGenie.count
}
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Profile"
} else {
return "About Genie"
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let tableCell = tableView.dequeueReusableCellWithIdentifier("myCell")
return tableCell!
}
}
and of course, I want to make it clickable so that it would go to its own viewController
After some suggestion, I changed most of my codes above and the result is still the same but this time it shows the header
The result is
airsoftFreak,
There are multiple mistakes I can figure out
There is no IBOutlet for your tableView which is added on top of your ViewController.
So you must be having something like
#IBOutlet var tableView: UITableView!
Your SettingsViewController only confirms to UITableViewDataSource and not to UITableViewDelegate. If you wamt to get didSelectRowAtIndexPath to be triggerred you have to confirm to UITableViewDelegate
class SettingsViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
As many have noticed and mentioned in their answer you will have to set your viewController as delegate for both UITableViewDelegate,UITableViewDataSource so
self.tableView.dataSource = self
self.tableView.delegate = self
The way you are instantiating cell is wrong as well :) Yopu should not create tableViewCell everytime for each cell :) Go to your TableView in storyBoard add a prototype cell, decorate it the way you want and the set the reusableIndentifier for it. Lets say reusableIndentifier you set is 'myCell'
your cellForRowAtIndexPath will change to
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//assuming you have different cells for each section
switch indexPath.section {
case 0: let tableCell = tableView.dequeueReusableCellWithIdentifier("myCell")
tableCell.textLabel.text = profile[indexPath.row]
return tableCell
//in swift switch has to be exhaustive so default
default: let secondSectionCell = tableView.dequeueReusableCellWithIdentifier("second_section_cell_identifier")
secondSectionCell.textLabel.text =aboutGenie[indexPath.row]
return secondSectionCell
}
}
Try to drag (ctrl+drag) the tableview to the yellow button at the top of the viewcontroller. You will now see to options: datasource and delegate. Choose one of these to and perform the action again for the other. Now the tableview should be linked to your code.
If the option to make it clickable was a question as well:
With the function didSelectRowAtIndexpath, you can achieve this. There should be a lot of stacks about this issue available.
You probably have not wired the UITableView delegate and dataSource methods to the viewController. You can do this in two ways.
1. programatically
create a tableViewOutlet
override fun viewDidLoad() {
super.viewDidLoad()
yourTableViewOutlet.delegate = self
yourTableViewOutlet.dataSource = self
}
in interfaceBuilder
a) open the document outline in the storyboard.
b) control drag from your tableView to your ViewController.
c) connect delegate and dataSource one by one.
click on the cell will fire the delegate method didSelectRowAtIndexPath
self.tableView.delegate and self.tableView.datasource
override func viewDidLoad() {
super.viewDidLoad()
let firstName = keychain[string: "first_name"]
profile = [
firstName!
]
self.tableView.delegate = self
self.tableView.datasource = self
}

Filling UITableView cell from remote database

I am facing an issue with UITableView.
I want to dynamically fill its cells with data fetched from a remote database, so it takes some times before the data arrived.
Here is the code:
class MailBoxViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var users: [NSDictionary] = []
override func viewDidLoad() {
super.viewDidLoad()
// call to rest API code to get all users in [NSDictionary]
(...)
// set table view delegate and data source
self.tableView.delegate = self
self.tableView.dataSource = self
}
// set number of sections within table view
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
// set number of rows for each section
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return self.users.count
}
return 0
}
// set header title for each section
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Users"
}
}
// set cell content for each row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// deque reusable cell
let cell = tableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as UITableViewCell
// set item title
if indexPath.section == 0 {
cell.textLabel?.text = self.users[indexPath.row]["firstname"] as? String
}
return cell
}
}
The problem is that when tableView functions are called to set number of rows for each section and to set cell content for each row, my [NSDictionary] users is still empty.
How could I do to set rows and cells only after my call to rest API code to get all users in [NSDictionary] is finished?
Thank you for any feedback.
Regards
When you get the response from the API, you should set self.users = arrayOfUsersYouReceivedFromServer and then call self.tableView.reloadData(). This
After users is populated, call tableView.reloadData(). This will call your data source methods again.
When you're done fetching the users call tableView.reloadData().

Resources