My first line of writing
Copy itself to the bottom lines
And when I scroll,
Places of data are changing
Data in UITableView Cell is repeated because Cell is reused so you will need to keep track of data for cell, may be you can add data in array arranged by index.
Yes thats the concept of UITableview dequeCells: as it reuses cells , so if there are say 100 entries only limited number of cells will be created at a time in order to save memory.
Now in order to avoid it, fill your cell views with datasource that has all the values.
If you see that some values are missing from datasource, just use, for example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Tek101TableViewCell
cell.selectionStyle = .none
cell.siraLabel.text = ""
cell.siraLabel.text = String(indexPath.row + 1) + ")"
return cell
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Tek101TableViewCell
cell.selectionStyle = .none
cell.siraLabel.text = String(indexPath.row + 1) + ")"
return cell
}
You need to set value in textfield delegate method as below :(As per your need)
If you are use return button in keyboard
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let cell = textField.superview?.superview as! Tek101TableViewCell // track you view hierarchy
cell. siraLabel?.text = textField.text
textField.resignFirstResponder()
return true
}
If you want to track value end editing method
func textFieldDidEndEditing(_ textField: UITextField){
let cell = textField.superview?.superview as! Tek101TableViewCell // track you view hierarchy
cell. siraLabel?.text = textField.text
}
In UITablview cell is reused so this thing will happen. To solve this issue you have to keep all the records inside one global array variable.
You can store all input in array or dictionary while user insertion. and then you have to give this array values to each and every cell from CellForRow.
In short you have to deal with array only.
Hope this will help you.
Related
I am making a form in a tableview.
Let's say I have 4 different types of cells, each being a question with different kind of answers
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if sortedFixedContentType.count != 0 {
let item = sortedFixedContentType[indexPath.row]
switch item.typeId {
case "1":
let cell = tableView.dequeueReusableCell(withIdentifier: "FirstCell", for: indexPath) as! FirstCell
return cell;
case "2":
let cell = tableView.dequeueReusableCell(withIdentifier: "SecondCell", for: indexPath) as! SecondCell
cell.customDelegate = self
return cell;
case "3":
let cell = tableView.dequeueReusableCell(withIdentifier: "ThirdCell", for: indexPath) as! ThirdCell
cell.commentsTextView.delegate = self
return cell;
case "4":
let cell = tableView.dequeueReusableCell(withIdentifier: "FourthCell", for: indexPath) as! FourthCell
return cell;
}
When the tableView is loaded I want to show only first cell, and depending on the answer different cells will be shown.
For example:
FirstCell can be answered with A, B, or C,
If I answer A SecondCell will be shown with answers X and Y.
If X is the answers ThirdCell will be shown (which has no options but a TextField), and when completed FourthCell will be shown
But if in FirstCell the answer is B or C only FourthCell will be directly shown.
At the moment I was doing it by changing the height of the rows in heightForRowAt, although I think there must be an easier way.
However I'm finding a problem:
If I get to the textField in ThirdCell and then I change my first answer, SecondCell is hidden but ThirdCell is not, as the condition to it was the second answer and it's already made, so I thought on setting the height of each row as condition to, but I don't know how to do it.
So I have two main questions:
Is it possible to access to the heightForRowAt to set it as a condition?
Should I make it this way? or maybe there's a better way to get what I need? I read about adding and deleting rows dynamically to tableviews but with the same cell type, this is why I decided to hide them by their height instead.
Thanks in advance!
I think the conventional method is to not modify the height but manipulate the data source (the number of rows in section etc.) to show/hide the appropriate cells.
You should update the data source appropriately after an event and then immediately after you can use func insertRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) and tableView.deleteRowsAt(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) to insert/delete cells in the tableView
This documentation might help: https://developer.apple.com/documentation/uikit/uitableview/1614879-insertrows
What I usually like to do is monitor a variable and when the variable is updated adjust the height of the cell. Make sure that your variable has this didSet code assigned to it so your tableview updates the height when the variable changes.
var selectedRow: Int = 999 {
didSet {
tableView.beginUpdates()
tableView.endUpdates()
}
}
And then, just like you have done I affect the height of the row inside of the heightForRow function.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == selectedRow { //assign the selected row when touched
let thisCell = tableView.cellForRow(at: indexPath)
if let thisHeight = thisCell?.bounds.height {
print("Bam we got a HEIGHT!!")
return thisHeight + 50
}
}
return 60 //return a default value in case the cell height is not available
}
I am currently working on a swift based HRM project. where it requires to show a tableview with slightly customized cell. cells it self containing two buttons, under some business logic one button would be hidden. for example ,
if the current user is the employee himself , he can see a list, the cell containing his name can see two buttons,but other cell would show simply one button.
i have tried the followings:
1. if the userId == employeeId (employeeId came from a model) then ,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ClaimTableViewCell", for: indexPath) as! ClaimTableViewCell
if(self.claimdata[indexPath.section].employeeId == self.empId) {
cell.CancelButton.isHidden = false
}
also , i have tried
if(self.claimdata[indexPath.section].employeeId != self.empId) {
cell.CancelButton.frame.size.height = 0
}
works fine for the first frame , the problem begins when i begin to scroll. for some unintended cell it also shows two buttons.
Am I missing something?
This issue is due to the cell reusability in UITableView.
Use below code in your cellForRowAtIndexPath method.
cell.CancelButton.isHidden = true
if(self.claimdata[indexPath.section].employeeId == self.empId) {
cell.CancelButton.isHidden = false
}
As tableView cell is reusableCell
dequeueReusableCell withIdentifier
you just need to give else condition so when it reuse the cell again it knowns what to do with CancelButton.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ClaimTableViewCell", for: indexPath) as! ClaimTableViewCell
if(self.claimdata[indexPath.section].employeeId == self.empId) {
cell.CancelButton.isHidden = false
}else{
cell.CancelButton.isHidden = true
}
}
I Have a UITableView which is controlled by NSFetchedResultsController. I want to add single cell to the first row and make this cell static. In other words, there will be a button which will open another View Controller.
Until now, I was ok with fetched results controller and table. Now I'm a bit confused. How should I do this?
Instead using a header might be ok too, but I don't want this header to be on top all the time. I want this cell to be just like WhatsApp iOS "Create new group" cell on chats panel.
Thank you!
var dataArray = ["A","B","C"]
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataArray.count+1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if indexPath.row == 0
{
let cell = tableView.dequeueReusableCell(withIdentifier: "CreateNewGroupCell") as! CreateNewGroupCell
return cell
}
else
{
// Get the data from Array
let data = self.dataArray[indexPath.row-1]
// Logic to show other cells
let cell = tableView.dequeueReusableCell(withIdentifier: "OtherCell") as! OtherCell
return cell
// ....
}
}
You will need to create tableview with number of rows fetched from NSFetchedResultsController +1. Also in cellForRowIndex method you will need to add a check like indexPath.row == 0 and in there you will make the changes.
Also you will have to add action for that button within that section. You can also set different custom tableview for first row.
It can be similar to following:
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
if(indexPath.row==0){
let cell = tableView.dequeueReusableCell(withIdentifier: "CellWithButton", for: indexPath) as! CellWithButton
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "OtherCells", for: indexPath) as! OtherCells
//here add data for cells from your array
}
return cell
}
I've got a tableView in the ViewController & an array called toDo, in the TableView I've a cell and in the cell I got textView.
the textView is editable (The user can change the text in the textView).
After the user changes the text - I want the cell to save it to the toDo array, but whenever I reloaddata the text disappears.
Here is what I tried:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath:indexPath) as! TableViewCell
cell.textField.text = toDo[indexPath.row]
cell.textField.delegate = self
return cell
}
**I have a got a test button that whenever I click it reload data.
Try this out - set the tag on the text field and implement textFieldDidEndEditing: to update your model before reloading the table view.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath:indexPath) as! TableViewCell
cell.textField.text = toDo[indexPath.row]
cell.textField.tag = indexPath.row
cell.textField.delegate = self
return cell
}
func textFieldDidEndEditing(textField: UITextField) {
toDo[textField.tag] = textField.text
}
I think that the problem is that, whenever the system needs to re-render the cell, the method func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) is called; that happens before your text view has a chance to save its content in the data model. In your case every time you press the button. I assume you save the content of the text field using optional func textFieldDidEndEditing(_ textField: UITextField) delegate method. You can add a println("SAVING") in such method, and a println("RE-RENDERING CELL") in the tableView(...) method and see the what the sequence of events is. Nots sure if this could help, but I would try that.
I have a UITableView with prototype cells that essentially takes up the whole page. I have a UIButton on the very bottom that should display a pop-up static UITableView when tapped. I'm struggling to account for the pop-up table view in cellForRowAtIndexPath.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let result: UITableViewCell
if tableView == self.tableView {
var cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel!.text = array[indexPath.row]
result = cell
} else if tableView == self.popUpTableView {
var popUpCell = self.popUpTableView.dequeueReusableCellWithIdentifier("popUpCell", forIndexPath: indexPath) as! UITableViewCell
popUpCell.textLabel!.text = popUpArray[indexPath.row]
result = popUpCell
}
return result
}
I'm getting an error at return result, where Variable 'result' used before being initialized, but I'm declaring it at the very top. Where am I going wrong with this?
You need to have exhaustive choices. It's possible that result never gets initialized because your checks are "if" and "else if". What happens if tableView is not either "self.tableView" or "self.popUpTableView"?
The simple fix is (if you only plan on having these two) to simply change your "else if" to a simple "else". This way result will always get initialized.