I try to display a simple cell with one label and an add item button. I can't get it to display correctly. I spent a lot of time but I can't find the solution.
This is the result:
There is no add item button and no correct row. I have just two items in coredata.
This is my code:
override func numberOfSectionsInTableView(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
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let data = items[indexPath.row] as Item
cell.textLabel?.text = data.body
return cell
}
What are the problems? Can anyone help me to display add item button, correct row count, and customize height of the cell correctly? Thanks in advanced.
First lets return the correct number of rows unto the table view.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellContent.count
}
Next lets get it to output unto the prototype cells.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
cell.textLabel?.text = cellContent[indexPath.row]
return cell
}
Also lets call the unto the view controller the following so that it can run properly.
ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
One last thing, your prototype's cell has the same identifier as the one in your code. In my code I would make sure that the identifier in the prototype cell is "Cell" since I called it in UITableViewCell(...reuseIdentifier: "Cell").
View picture to see where to add the identifier in your storyboard
Related
After much searching and reading I unfortunately do not come from the following. I want to use static tables to display certain data. (Are there better options?)
In my view I first put an onion picture with a container view underneath. The container view again refers to a Table View Controller.
I made an outlet from the cells and then I thought I could easily adjust the text.
Now I want to change the text of the fields in the table, but unfortunately I do not succeed.
When I start the app then the table is completely empty as seen on the screenshot.
What am I doing wrong ?
class TableViewController: UITableViewController {
var data: [String] = ["Muis", "Aap", "Koe", "Vis"]
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
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
return data.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let iets = data[indexPath.row]
cell.textLabel?.text = iets
return cell
}
}
If you want to use static cells
Forget dequeueing UITableViewCell instances and all tableview data source and delegate methods.
In Interface Builder select the table view and select Static Cells from the Content popup
Drag the amount of static cells you need into the canvas
In the view controller declare IBOutlets and connect them directly to the UI elements in the cells
You need to change your way of thinking for this one. You do not own the cells, the UITableView does. It will provide cells as it seems fit by using your implementations of UITableViewDataSource:
func numberOfSections(in: UITableView) -> Int
func tableView(UITableView, numberOfRowsInSection: Int) -> Int
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
Normally, the texts (your actual data) would be held in a list available to this data source.
Example:
var data: [String] = []
// Other functions
func numberOfSections(in: UITableView) -> Int {
return 1
}
func tableView(UITableView, numberOfRowsInSection: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YOUR_IDENTIFIER")
cell.text = data[indexPath.row]
return cell
}
Now, if you want to change this cell's text, all you have to do is update your data list and reload the data.
What I have done after a lot of testing and reading. I have create a segue to the statutable class.
if (segue.identifier == "myEmbeddedSegue") {
let childViewController = segue.destination as! hondDetialTableViewController
childViewController.hondId = hondData["hondId"]!
}
In this segue I send only the hondId, everything else i ask entities.
I'm sorry but this is not at all how UITableView works. The UITableViewCell that you define in the Xib/Storyboard within the tableview are just "models" or templates, they don't actually exists until you dequeue them.
You can read how UITableView works here: http://www.thomashanning.com/uitableview-tutorial-for-beginners/
You have to return numberOfSections > 0 if you want anything displayed in your tableview; similarly, that section has to also have numberOfRows > 0 otherwise again, nothing will be displayed (ok, maybe headers and footers if those are properly setup).
At any rate, cells are only accessible after you dequeue them. Creating an outlet in a XIB to a UITableViewCell is useless in most cases.
You can explore other options, such as UIStackView, or maybe what you need is just plain custom UIView with labels that you properly set and layout using NSLayoutConstraints. There are plenty of resources out there, this is just one I quickly Googled for you to get started: https://www.youtube.com/watch?v=de0sthle44I
Good Luck.
i have a uiviewcontroller, where i put a uitableview, with custom cells. Each of these cells, have inside a uicollectionview, with a horizontal scrolling.
I am working programmatically, so the tableviewcell is the delegate/datasource for collectionview.
My datasource structure, is an array of arrays.
My tableview, has, for every tableviewcell, a tableview section.Tableview datasource methods work without problem using the array. I am able to set up the tableview correctly.
Inside this cell, i want to display a collectionview, with horizontal scrolling.
The problem which i am encountering is i cannot assign datasource correctly using the arrays to collectionview. What happens is when i load the tableview, and scrolling down, i see duplicate records, so for example the first 3 rows are displayed correctly(in terms of data and number of items),but from the 4th row, for example, data is duplicated, so i see again the records for the first 3 rows, and of course this does not have to happen, because if i scroll on the 4th line(the collectionview on the 4th row) the app crashes due to an index problem out of range.
This is simple to understand: lets say i have 10 cells on the first row, and 5 on the 4th row/or section, as you prefer... Scrolling the 4th row'scollection view will cause the crash. This is because of the wrong data which is actually the same from the first row: instead, i have only 5 cells to render...
Tecnique i am using is pretty simple: give each tableviewcell's tag the actual indexpath.section. Then in the collectionview, use this tag to loop through array and then the indexpath.item to get the correct array.
Lets get a look into the code now:
Tableview
func numberOfSections(in tableView: UITableView) -> Int {
return DataManager.shared.datasource.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! DiarioTableViewCell
cell.tag = indexPath.section
return cell
}
TableviewCell
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: "cell")
collectionView.register(DiarioCVCell.self, forCellWithReuseIdentifier: "cellCV")
collectionView.delegate = self
collectionView.dataSource = self
DataManager.shared.istanzaCV = self.collectionView
addSubview(tableCellBG)
addSubview(collectionView)
setConstraints()
}
CollectionView
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let tvCell = collectionView.superview as! DiarioTableViewCell
return DataManager.shared.datasource[tvCell.tag].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellCV", for: indexPath) as! DiarioCVCell
let celltag = (collectionView.superview as! DiarioTableViewCell).tag
cell.datasource = DataManager.shared.datasource[celltag][indexPath.item]
return cell
}
Notice i have read all the possibly related threads, even ashfurrow's article, and here on stack, but i was not able to find a solution.
Thanks for any help!
Try to reload your collection view when cell is showing. Cells are being reused to save memory so they are created only once - you are setting all data at init and it's staying there forever.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! DiarioTableViewCell
cell.tag = indexPath.section
cell.collectionView.reloadData()
cell.collectionView.collectionViewLayout.invalidateLayout() //just to be sure, maybe it's not necessary
return cell
}
Requirement :
I have a list of UITableviewCell loaded from a nib that I'm presenting on UITableview. The first time I open the UIViewController all cells are shown correctly and work as expected.
Issue :
If I navigate back to the parent and then open the UIViewController again the UITableviewCell are 'invisible'. I say invisible because with a breakpoint in cellForRowAt I can see that the table view does load all cells and the cells are valid.
Code :
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 13
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = (project?.sliderData.sliders[indexPath.row].view)! as UITableViewCell
print(cell.contentView.subviews.count)
if let left = cell.viewWithTag(2) as? UILabel {
left.text = "left"
}
if let middle = cell.viewWithTag(3) as? UILabel {
middle.text = "middle"
}
if let right = cell.viewWithTag(4) as? UILabel {
right.text = "right"
}
return cell
}
Screen Shot Image
Expected observation :
I was thinking that maybe the subviews of the cells get released because I don't have any bindings to them in IB. To test this I'm printing the count of subviews and writing some text to the subview labels. And everything seems to go fine, the cells are loaded and the labels are there but the cells just don't show up.
But then, if I scroll the TableView up and down a little to get some cells updated those cells do appear at the top and bottom of the view as shown in the pic.
You need to call dequeueReusableCell(withIdentifier: "cell") inside your code then will show your table cell. It will reuse cell for your all numbers of row data content.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
return cell
}
More Details : How to create uitableview with multiple sections in iOS Swift.
Did not find reason why the tableView behaves the way it does so I solved the issue by dequeueing default cells. The views provided by the slider objects are added as subviews to the dequeued cells. Now the subviews can of course be any UIViews.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "sliderCell")
if cell == nil {
cell = UITableViewCell.init(style: .default, reuseIdentifier: "sliderCell")
}
cell?.addSubview((project?.sliderData.sliders[indexPath.row].view)!)
return cell!
}
I'm putting UISplitViewControllers in UITabBarController.
I'm Trying to use the master view as a filter. So I used cellAccessoryType as check mark . Only one among all can be selected. the code that i wrote for this is
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.selectedIndex = indexPath.row
let cell:UITableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)!
cell.accessoryType = .Checkmark
self.performSegueWithIdentifier("dispAccounts", sender: self)
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let cell:UITableViewCell = self.tableView.cellForRowAtIndexPath(indexPath)!
cell.accessoryType = .None
}
override func viewDidLoad() {
super.viewDidLoad()
filterList = ["All Accounts","Business Accounts","Person Accounts"]
self.tableView.allowsMultipleSelection = false
//self.splitViewController?.maximumPrimaryColumnWidth = 140; //This line is to restrict the width of master View of UISplitVC
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(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
return 3
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("accountCell", forIndexPath: indexPath)
cell.textLabel?.text = filterList[indexPath.row]
return cell
}
Now Once I select 'All Account' cell,Then I move to another tab 'Call'
Then I comeback to 'Account' tab, then I select 'Business Accounts' it is getting selected and checkmark is also updating but the problem is 'All accounts' cell's check mark is not getting vanished.
This bug occurs due to the optimisations that have been implemented into UITableView and UITableViewCell. These two views are highly efficient and one way that Apple has made them so efficient is by reusing the cells instead of instantiating new cells all the time (that's why you are calling dequeueReusableCellWithIdentifier instead of instantiating a new cell every time).
In order to overcome this bug, then you have to reset the cells every time they are used.
This can be done in two ways:
OverwritingprepareForReuse if you were subclassing UITableViewCell (but this is not an option for you since you are using the standard UITableViewCell)
Resetting the properties directly in cellForRowAtIndexPath
So a possible solution for you could look like the following:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Getting the cell
let cell = tableView.dequeueReusableCellWithIdentifier("accountCell", forIndexPath: indexPath)
// Resetting the cell
cell.textLabel?.text = ""
cell.selected = false
// Configuring the cell
cell.textLabel?.text = filterList[indexPath.row]
// Returning the finished cell
return cell
}
My Situation: I want to save Data from an Array at Index X in an Row on Index X in Section 1.
My code is:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return setObjectToPass.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Section \(section)"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellEmpty = tableView.dequeueReusableCellWithIdentifier("LabelCell")
var countCell = 0
while countCell < setObjectToPass.count {
let indexPaths = NSIndexPath(forRow: countCell, inSection: 0)
let cell = tableView.dequeueReusableCellWithIdentifier( "LabelCell", forIndexPath: indexPaths)
cell.textLabel!.text = String(setObjectToPass[countCell])
print(cell)
countCell+=1
return cell
}
My Problem is that only the first index of the Array SetObjectToPass is passed and set into the Cell.text
while counter < fetchResult?.count {
let set = fetchResult![counter]
counter+=1;
setObject.append((set.reps?.integerValue)!)
}
You are implementing the tableView(_:cellForRowAtIndexPath:indexPath:) method wrongly.
Remember, every delegate method in UITableViewDelegate is like asking you a question. For example, numberOfSectionsInTableView(_:) is like asking you "How many sections do you want in your table view?". You answer the question by returning a value.
tableView(_:cellForRowAtIndexPath:indexPath:) is similar. It asks a question as well. It asks "What should I display in the table row at this index path?"
In your code, it seems like you want to give multiple answers - looping through the array and attempting to return multiple times. But it doesn't work that way, you can only give one answer.
In the first iteration of the while loop, the execution hits return and stopped. That's why you only see the first table cell.
Thus, you should change your code so that it only gives one answer to the question:
let cell = tableView.dequeueReusableCellWithIdentifier("LabalCell")
cell.textLabel?.text = String(setObjectsToPass[indexPath.row])
return cell
Don't use the loop in cellForRowAtIndexPath delegate method. cellForRowAtIndexPath method call each row based upon numberOfRowsInSection count rows, simply use indexpath.row, Use this code,
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cellEmpty = tableView.dequeueReusableCellWithIdentifier("LabelCell")
let cell = tableView.dequeueReusableCellWithIdentifier( "LabelCell", forIndexPath: indexPaths)
cell.textLabel!.text = setObjectToPass[indexPath.row] as? String
return cell
}
hope its helpful