I have a tableView connected to the array. The array isn't empty but the tableView shows nothing other than lines. Can you help me, please?
var invoicesDictionary = [String:[String]]()
var invoicesSectionTitles = [String]()
var invoices: [String] = ["JV1", "R1", "JV2"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
for invoice in invoices {
let invoiceKey = String(invoice.prefix(1))
if var invoiceValues = self.invoicesDictionary[invoiceKey] {
invoiceValues.append(invoice)
self.invoicesDictionary[invoiceKey] = invoiceValues
} else {
self.invoicesDictionary[invoiceKey] = [invoice]
}
}
invoicesSectionTitles = [String](invoicesDictionary.keys)
invoicesSectionTitles = invoicesSectionTitles.sorted(by: { $0 < $1 })
tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return invoicesSectionTitles.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let invoiceKey = invoicesSectionTitles[section]
if let invoiceValues = invoicesDictionary[invoiceKey] {
return invoiceValues.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let invoiceKey = invoicesSectionTitles[indexPath.section]
if let invoiceValues = invoicesDictionary[invoiceKey] {
cell.textLabel?.text = invoiceValues[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return invoicesSectionTitles[section]
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return invoicesSectionTitles
}
I printed out the array which is without a doubt not empty. I tried this code on an another project and it just worked.
You have to put this in viewDidLoad :
tableView.delegate = self
tableView.dataSource = self
You have to tell the View controller that it's the delegate or assigned VC for the table View functionality by passing the following lines inside viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
Related
I am currently building an Application which shows all the faculties that are available in my college. I have the data in a .plist file which I have read and populated in the UITableView. I am currently trying to show some details such as prefix and Name in the cell which when clicked on Expands to show more details about the faculty.
My TableViewController Code is shown below:
struct cellData {
var opened = Bool()
var Name = String()
var Prefix = String()
var sectionData = [String]()
}
class TableViewController: UITableViewController {
//MARK: - Initialize variables
var dataManager = DataManager()
private var header = [String]()
private var facultyDetails = [[String: String]]()
var tableViewData = [cellData]()
#IBOutlet var facultyTableView: UITableView!
//MARK: - view did load
override func viewDidLoad() {
super.viewDidLoad()
for data in dataManager.allSheet1s() {
let dataInCell = cellData(opened: false, Name: data.item2!,
Prefix: data.item1!, sectionData [data.item0!,
data.item2!,
data.item1!,
data.item3!,
data.item4!,
data.item5!,
data.item6!,
data.item7!,
data.item8!,
data.item9!,
data.item10!
])
tableViewData.append(dataInCell)
facultyTableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "CustomCell")
}
}
// MARK: - Table view delegate
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return tableViewData.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
if indexPath.row == 0 {
cell.EmpID.text = tableViewData[indexPath.section].sectionData[0]
cell.Name.text = tableViewData[indexPath.section].sectionData[1]
cell.Prefix.text = tableViewData[indexPath.section].sectionData[2]
cell.SchoolName.text = tableViewData[indexPath.section].sectionData[3]
cell.BuildingName.text = tableViewData[indexPath.section].sectionData[4]
cell.FloorNo.text = tableViewData[indexPath.section].sectionData[5]
cell.CabinLocation.text = tableViewData[indexPath.section].sectionData[6]
cell.RoomNo.text = tableViewData[indexPath.section].sectionData[7]
cell.CabinNo.text = tableViewData[indexPath.section].sectionData[8]
cell.IntercomNumber.text = tableViewData[indexPath.section].sectionData[9]
cell.Email.text = tableViewData[indexPath.section].sectionData[10]
return cell
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableViewData[indexPath.section].opened == true {
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .right)
}
else {
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .left)
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if tableViewData[indexPath.section].opened == true {
return 400
}
else {
return 70
}
}
}
Simulator
Physical Device
Right now you can see in the simulator that it works perfectly fine. But when I load it up in the physical device the height of each cell in the rows are clipping through the other cells below it.
Modify your cell for row code by adding cell.clipsToBounds = true
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
cell.clipsToBounds = true
if indexPath.row == 0 {
cell.EmpID.text = tableViewData[indexPath.section].sectionData[0]
cell.Name.text = tableViewData[indexPath.section].sectionData[1]
cell.Prefix.text = tableViewData[indexPath.section].sectionData[2]
cell.SchoolName.text = tableViewData[indexPath.section].sectionData[3]
cell.BuildingName.text = tableViewData[indexPath.section].sectionData[4]
cell.FloorNo.text = tableViewData[indexPath.section].sectionData[5]
cell.CabinLocation.text = tableViewData[indexPath.section].sectionData[6]
cell.RoomNo.text = tableViewData[indexPath.section].sectionData[7]
cell.CabinNo.text = tableViewData[indexPath.section].sectionData[8]
cell.IntercomNumber.text = tableViewData[indexPath.section].sectionData[9]
cell.Email.text = tableViewData[indexPath.section].sectionData[10]
return cell
}
return cell
}
And override a method
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 70
}
I created a UIViewController containing a UITableView.
I want it to display files I created at another position.
The corresponding class looks like this:
import UIKit
class BackupViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var rows:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.table.dataSource = self
self.table.delegate = self
getFiles()
// Do any additional setup after loading the view.
}
func getFiles() {
let filemgr = FileManager.default
let path = filemgr.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last
do {
rows = try filemgr.contentsOfDirectory(atPath: (path?.path)!)
// process files
} catch {
print("Error while enumerating files")
}
table.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rows.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.table.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
cell.textLabel?.text = rows[indexPath.row]
return cell
}
#IBOutlet weak var table: UITableView!
}
It reads the files correctly and when
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
is called (which happens 3 times for some reason), the array has (at the moment) 2 elements. However, the function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
is never called and of course I get an empty table.
You need to reload your tableView after fetching files in getFile() function.
func getFiles() {
let filemgr = FileManager.default
let path = filemgr.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last
do {
rows = try filemgr.contentsOfDirectory(atPath: (path?.path)!)
self.table.reloadData()
// process files
} catch {
print("Error while enumerating files")
}
/*for file in files {
rows.append(file.absoluteString)
}*/
showFiles = true
}
You need to call reloadData() once row will have some value in it.
I'm trying to create a tableView within a viewController. I know it is annoying, but the table looks much better that way. I am also trying to incorporate data from Firebase to put into the table. Unfortunately when I run the code, it only shows a blank table. The console was able to print the desired data, but it just won't show on the actual table. Please let me know what I'm doing wrong. Many thanks!
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var user = Auth.auth().currentUser
var users = [Users]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.reloadData()
fetchUser()
}
func fetchUser() {
Database.database().reference(fromURL: "https://yala-2018.firebaseio.com/").child("users").observe(.childAdded, with: { (DataSnapshot) in
if let dictionary = DataSnapshot.value as? [String: AnyObject] {
let user = Users()
// user.setValuesForKeys(dictionary)
user.name = dictionary["name"] as! String
user.age = dictionary["age"] as! String
user.sex = dictionary["sex"] as! String
self.users.append(user)
print(user.name, user.age)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "guide", for: indexPath)
let user = users[indexPath.row]
cell.textLabel?.text = user.name
cell.detailTextLabel?.text = "Age: \(user.age) Sex: \(user.sex)"
// Configure the cell...
return cell
}
func tableView(tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
Change this to 1 as by 0 you mean no sections which will display empty tableView even if there is a data , or remove it as by default it's 1
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
I am having a weird issue where for some reason my UITableView is not being reloading after performing a search. The console prints out the correctly filtered data, but the table simply doesn't change. I have never encountered this issue, so I first attempted the solutions which naturally came to mind:
Tried tableView.reloadData() in the Main Queue
Quit Xcode, clean build, reinstall
Cleared out the derived data dir
I have found several similar issue in SO, but all of the solutions I've seen are things I've tried, mainly reloading tableview in main queue.
Hoping maybe I just simply have an issue in my code or something I'm missing.
I am running Xcode 8.3.3
import UIKit
class CategoriesViewController: UIViewController {
var isFiltering = false
var location = Location()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var categoriesSearchResults = [Category]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.allowsSelection = true
tableView.keyboardDismissMode = .onDrag
let nib = UINib(nibName: "CategoryTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier:"CategoryTableViewCell");
searchBar.returnKeyType = UIReturnKeyType.done
searchBar.autocapitalizationType = .none
searchBar.delegate = self
}
extension CategoriesViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("HI")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering {
return self.categoriesSearchResults.count
}
return self.location.categories.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let cell = self.tableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as? CategoryTableViewCell {
var category: Category
if isFiltering {
category = self.categoriesSearchResults[indexPath.row]
} else {
category = self.location.categories[indexPath.row]
}
cell.name.text = category.name
cell.status.textColor = UIColor.lightGray
cell.status.text = "Not Verified"
}
return cell
}
}
extension CategoriesViewController : UISearchBarDelegate {
func searchBarIsEmpty() -> Bool{
return self.searchBar.text?.isEmpty ?? true
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.isFiltering = true
self.categoriesSearchResults.removeAll()
tableView.reloadData()
self.view.endEditing(true)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBarIsEmpty() {
self.view.endEditing(true)
self.isFiltering = false
} else {
self.isFiltering = true
self.categoriesSearchResults = self.location.categories.filter({ (category: Category) -> Bool in
return category.name.lowercased().contains(searchText.lowercased())
})
}
tableView.reloadData()
}
}
and my custom table view cell:
import UIKit
class CategoryTableViewCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var status: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func prepareForReuse() {
super.prepareForReuse()
self.name.text = ""
self.status.text = ""
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Thank you in advance.
EDIT: Might also be worth mentioning, when I am actively searching, the function tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) is not called??
The scope of if let nests in its scope. In your code you are always returning let cell = UITableViewCell(). Try returning it inside the if let :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let cell = self.tableView.dequeueReusableCell(withIdentifier: "CategoryTableViewCell", for: indexPath) as? CategoryTableViewCell {
var category: Category
if isFiltering {
category = self.categoriesSearchResults[indexPath.row]
} else {
category = self.location.categories[indexPath.row]
}
cell.name.text = category.name
cell.status.textColor = UIColor.lightGray
cell.status.text = "Not Verified"
/// RETURN CELL HERE
return cell
}
return cell
}
I have the following code to display two tables populated from two different arrays in one view:
#IBOutlet var RFTable: UITableView
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
}
override func viewDidLoad() {
super.viewDidLoad()
self.RFTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return self.RFArray.count;
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell:UITableViewCell = self.RFTable.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel.text = String(self.RFArray[indexPath.row])
return cell
}
#IBOutlet var IMProdTable: UITableView
func tableView2(IMProdTable: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
}
override func viewDidLoad() {
super.viewDidLoad()
self.IMProdTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell2")
}
func tableView2(IMProdTable: UITableView!, numberOfRowsInSection section: Int) -> Int {
return self.IMProdArray.count;
}
func tableView2(IMProdTable: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
var cell2:UITableViewCell = self.IMProdTable.dequeueReusableCellWithIdentifier("cell2") as UITableViewCell
cell2.textLabel.text = String(self.IMProdArray[indexPath.row])
return cell2
}
I got the first table working, and then copied and pasted the text, replacing the array names and tableview names, and have hooked up the delegate and datasource. However Xcode displays 'invalid redeclaration of viewdidload' on the second (pasted) code. If I replace this to 'fund loadView() {' instead of viewdidload the app builds. When I test it though, both tables view exactly the same data which is the data in 'RFArray.' I am VERY new to coding and cannot see what I have done, please help.
#IBOutlet var RFTable: UITableView
#IBOutlet var IMProdTable: UITableView
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
}
override func viewDidLoad() {
super.viewDidLoad()
self.RFTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.IMProdTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell2")
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
if tableView == RFTable {
return self.RFArray.count;
} else {
return self.IMProdArray.count;
}
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
if tableView == RFTable {
var cell:UITableViewCell = self.RFTable.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel.text = String(self.RFArray[indexPath.row])
return cell
} else {
var cell2:UITableViewCell = self.IMProdTable.dequeueReusableCellWithIdentifier("cell2") as UITableViewCell
cell2.textLabel.text = String(self.IMProdArray[indexPath.row])
return cell2
}
}
Just a quick edit. You need to keep the delegate and datasource methods same and check which TableView instance is actually sending the message.
You cannot override the same method twice in a derived class.
First create two DataSource implemented classes
First Data source
class FirstDataSouce: NSObject,UITableViewDataSource,UITableViewDelegate {
var items: [String] = []
override init(){
super.init()
}
func setData(items:[String]){
self.items = items
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RecentTableViewCell") as! RecentTableViewCell
cell.titleLabel.text = items[indexPath.row]
return cell
}
}
Second Data source
class SecondDataSouce: NSObject,UITableViewDataSource,UITableViewDelegate {
var items: [String] = []
override init(){
super.init()
}
func setData(items:[String]){
self.items = items
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "RecentTableViewCell") as! RecentTableViewCell
cell.titleLabel.text = items[indexPath.row]
return cell
}
}
Set datasource to tableview in ViewController
class ViewController: UIViewController{
#IBOutlet weak var tableView1: UITableView!
#IBOutlet weak var tableView2: UITableView!
var dataSource1: FirstDataSouce!
var dataSource2: SecondDataSouce!
func prepareTableViews(){
let items1 = [“a”,”b”,”c”]
dataSource1 = FirstDataSouce()
dataSource1.setData(items: items1)
self.tableView1.dataSource = dataSource1
self.tableView1.delegate = dataSource1
self.tableView1.register(SelectorTableViewCell.self,
forCellReuseIdentifier:
"TableViewCell")
self.tableView1.tableFooterView = UIView()
let items2 = [“1”,”2”,”3”]
dataSource2 = SecondDataSouce()
dataSource2.setData(items: items2)
self.recentTableView.dataSource = dataSource2
self.recentTableView.delegate = dataSource2
self.recentTableView.register(RecentTableViewCell.self,
forCellReuseIdentifier:
"TableViewCell")
self.recentTableView.tableFooterView = UIView()
}
}
Also Make Sure To reload each TableView After fetching data to TableviewCell.
e.g
#IBOutlet var RFTable: UITableView
#IBOutlet var IMProdTable: UITableView
override func viewDidLoad() {
super.viewDidLoad()
self.RFTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell1")
self.IMProdTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell2")
RFTable.reloadData()
IMProdTable.reloadData()
}