Dynamically add subViews to UITableViewCell - swift

I need to add UILabels to UITableViewCell, but its dynamic, first cell can have 1 label, second can have 4 and I dont know before hand. So I tried this
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell: ReviewTableViewCell = reviewTableView.dequeueReusableCell(withIdentifier: "Review", for: indexPath) as! ReviewTableViewCell
var reviewObj:Review!
reviewObj = reviewArray[(indexPath as NSIndexPath).row]
let viewsAdded = commentViewsAddedDict[indexPath.row]
if(viewsAdded == nil)
{
for comment in reviewObj.commentArray
{
let label1 = UILabel()
label1.text = “text1”
label1.textColor = UIColor(hexString: "#333333")
let label2 = UILabel()
label2.text = “text2”
label2.numberOfLines = 0
label2.sizeToFit()
label2.textColor = UIColor(hexString: "#666666")
let label3 = UILabel()
label3.text = "----------------------------------------------------------------------"
label3.textColor = UIColor(hexString: "#eeeeee")
cell.stackView1.addArrangedSubview(label1)
cell.stackView1.addArrangedSubview(label2)
cell.stackView1.addArrangedSubview(label3)
}
commentViewsAddedDict[indexPath.row] = true
}
return cell
}
But what happens, the previously added views are not removed and it again tries to add new views.
So I want to know, what is the efficient way to do this.
Secondly, where I am going wrong.
Regards
Ranjit

You are using commentViewsAddedDict to figure out whether rows have been added or not. But whether these labels were added or not is not a function of the row in the table, but rather of the cell, which is reused.
So, I'd would advise:
eliminate this commentViewsAddedDict logic; and
move the logic regarding how many labels have been added to ReviewTableViewCell.
So, you might end up with:
func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -> UITableViewCell {
let cell = reviewTableView.dequeueReusableCell(withIdentifier: "Review", for: indexPath) as! ReviewTableViewCell
var reviewObject = reviewArray[(indexPath as NSIndexPath).row]
cell.updateLabels(for: reviewObject)
return cell
}
And in ReviewTableViewCell:
func updateLabels(for reviewObject: ReviewObjectType) {
// add label if needed
// update label `text` if needed
// remove any labels that need to be removed
}
It's a little hard to be specific on the logic in updateLabels as the provided code snippet in the question is unclear, but the basic idea is that the ReviewTableViewCell should keep track of whether its labels have been added or not, and on the basis of the reviewObject, decide whether it needs to add a label, update an existing label, or remove any labels not needed for this particular reviewObject. But all of this "label state" logic is a function of the cell (which can be reused), not of which row in the table to which the cell corresponds.

Related

Table view cell get initial value after scrolling

I have a table view and 10 different prototype cell. I used storyboard and created custom UITableCell class for each cell.
There is a checkbox in CheckBoxCell. I created these checkboxes in loop according to options count.
Problem is that after I checked a checkbox, checkbox value changes but when I scroll up or down the table view, checkbox value changes with inital value.
I investigated some questions in stackoverflow. I live this problem, because of after every scroll dequeReusebleCell works and re-create the cell in the queue. I tried to use these solutions, but I cannot succeeded.
I am new for Swift and I don't know how can I solve this problem.
May someone tell me how can I solve this problem and what is the correct approach?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let object = surveyDetailArray.first!.elements[indexPath.row]
switch object.type {
case CellConfig.checkbox.rawValue:
let cell = tableView.dequeueReusableCell(withIdentifier: "CheckboxCell", for: indexPath) as! CheckboxCell
let object = surveyDetailArray.first!.elements[indexPath.row]
for label in object.options {
cell.checkbox = BEMCheckBox()
cell.checkbox.onAnimationType = .bounce
cell.checkbox.offAnimationType = .bounce
cell.checkbox.boxType = .square
cell.checkbox.onFillColor = .red
cell.checkbox.offFillColor = .white
cell.checkbox.onCheckColor = .white
cell.checkbox.delegate = self
cell.checkbox.tag = label.id
cell.contentView.addSubview(cell.checkbox)
}
return cell
}
In your problem the cell is dequeuing and is getting back to the old state, that's will obviously happen because of dequeReusableCell,
now for the solution, you should use a model to store the states of different checkboxes and on cellforRowAt
add the code for checkbox persistance according to the model, when you enable a checkbox,
change the value of the variable in the model also and leave rest it on your cellForRowAt code. I'm adding a small example for your understanding, hope it helps.
CODE
Struct ButtonsStates {
var isButtonEnabled : Bool = false
}
// In your ViewController use the above model for saving buttonValues
var buttonStates : [ButtonStates]? // initialize as many as the rows
// in cellForRowAt
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ExampleCell", for: indexPath) as? ExampleCell else { return fatalError("") }
// here if the cell is dequeued still when cell will again be visible then this condition will be checked
cell.customButton.isSelected = buttonStates[indexPath.row].isButtonEnabled
return cell

Set action for subview inside a table cell (swift)

When i clicked the subview,it didn't trigger subview's action. But i have selected whole cell. How to fix it?
Here is my code.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("idcell", forIndexPath: indexPath) as UITableViewCell
let lblTitle : UILabel = cell.contentView.viewWithTag(101) as! UILabel
lblTitle.text = (deptId[indexPath.row] as? String)! + " " + (deptDesc[indexPath.row] as? String)!
var height:CGFloat = 40
if(indexPath.row == departmentSelectedRow){
for i in 0...deptProfile.count-1{
let label = UILabel(frame: CGRectMake(0,height,400,30))
label.targetForAction("sadasdd", withSender: nil)
height = height+40
label.text = ("ewrewrewre")
label.backgroundColor = UIColor.redColor()
cell.addSubview(label)
}
}
return cell
}
If you want to interact with something inside a cell (rather than select the whole cell) then you should use a button or a gesture recogniser. These are properly interactive elements that you can add a target and action to.
You should also change how you're using cells, specifically:
Use custom cell classes, so you can directly reference the cell elements instead of using viewWithTag which is bad practice
Use multiple different cell classes (and thus identifiers) so you can use actually different cells for selected and unselected rows
Don't create and add subviews on the fly, this is what the different cell classes are for

Center text around specific word in table cell using Swift

I have a UITableView with multiple cells, each containing a sentence of attributed text. I would like to know how to centre the text in each of these cells around a specific word contained in those sentences.
The result would be a central column of the word but each cell containing different surrounding words.
I'm guessing the place to start is to centre the text in the cell array but doing so in the "cellForRowAtIndexPath" delegate method with the line:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
self.cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
if(cell.isEqual(NSNull)) {
self.cell = NSBundle.mainBundle().loadNibNamed("cell", owner: self, options: nil)[0] as! UITableViewCell;
}
self.cell.detailTextLabel?.text = self.synonymsCategories[indexPath.row]!
self.cell.textLabel?.attributedText = self.synonymsContext[indexPath.row]
self.cell.detailTextLabel?.textAlignment = .Right
self.cell.textLabel?.textAlignment = NSTextAlignment.Center
self.cell.textLabel?.backgroundColor = UIColor.greenColor()
return self.cell as UITableViewCell
}
But no changes I make to "self.cell.textLabel" produce any visual changes at run time.
Any ideas? Any help much appreciated.
Felix

problems with UITableView and reuseablecells

so i am experiencing two problems right now with my (pretty basic) tableview. i will paste my code below but here is the general gist: i'm making a dictionary app, so each cell contains two labels for the two languages. if a cell gets tapped, i'd like for: 1. cell height increase, 2. font size increase, 3. text align center, 4. cell background change color.
i've also included code to 'reset' a selected cell if it is selected a second time - this seems to be causing my second problem
the problem is when a cell is selected:
those four things happen to cells which are currently offscreen.
the program crashes when: i select cell 0, the cell changes which is fine, but when i click on any cell that is offscreen at the time (say cell 10) the program will crash giving me this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// reset previous cell but only if selectedRow isn't -1 which is initial value
if selectedRowIndex != -1{
let previousCell = convoTable.cellForRowAtIndexPath(NSIndexPath(forRow: selectedRowIndex, inSection: 0)) as! PhraseTableViewCell
previousCell.backgroundColor = UIColor.whiteColor()
previousCell.lang2Label.font = UIFont(name: (previousCell.lang2Label?.font?.fontName)!, size: (previousCell.lang2Label?.font?.pointSize)! - 5)
previousCell.lang2Label.textAlignment = .Right
}
//make changes to new cell that was pressed upon
if selectedRowIndex != indexPath.row{
print("new cell got selected")
cellSelected = true
// save the selected index
self.selectedRowIndex = indexPath.row
let cell = self.convoTable.cellForRowAtIndexPath(indexPath) as! PhraseTableViewCell
cell.lang2Label.font = UIFont(name: (cell.lang2Label?.font?.fontName)!, size: (cell.lang2Label?.font?.pointSize)! + 5)
cell.backgroundColor = UIColor.cyanColor()
cell.lang2Label.textAlignment = .Center
// update the height for all the cells
self.convoTable.beginUpdates()
self.convoTable.endUpdates()
cell.lang1Label.frame = CGRectMake(3,0, cell.bounds.width, cell.bounds.height/3)
cell.lang2Label.frame = CGRectMake(-3,cell.bounds.height/3, cell.bounds.width, cell.bounds.height/3 * 2)
}
else {
print("same cell pressed")
cellSelected = false
selectedRowIndex = -1
}
self.convoTable.beginUpdates()
self.convoTable.endUpdates()
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! PhraseTableViewCell
cell.lang1Label.text = items![indexPath.row].eng
cell.lang2Label.text = items![indexPath.row].prs
return cell
}
thank you all very much, this help is much appreciated.
so there is no real answer to the questions i posed, the best workaround is to just have another array which maintains the states in the cells. sucks i know but really the best "solution"

Text overlapping itself in table cells SWIFT

I have some text which goes into UIlabels I have created inside a table view cell. When these table view cells become updated the text overlaps itself, almost like the previous text what was there has not been removed, like so:
code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell",forIndexPath: indexPath) as! UITableViewCell
var nameLabel = UILabel(frame: CGRectMake(cell.frame.size.width * 0.040, cell.frame.size.height * 0.22, cell.frame.size.width * 0.735, cell.frame.size.height * 0.312))
var userAtIndexPath = finalMatchesBlurUser[indexPath.row]
nameLabel.text = userAtIndexPath.username.uppercaseString
cell.addSubview(nameLabel)
}
The finalMatchesBlurUser is a PFUser fetched from Parses database that will be changing, when this change is what causes the names to overlap.
Can anyone point out why this is happening?
Everytime you update the tableview, it checks the queue to see if it can reuse a cell instead of initializing a new one. In this case, when it updates, it has cells in the queue so you're adding a new label subview everytime the table updates which is causing this effect. In this case, you should only add the label subview if it doesn't exist already. Otherwise, just update the text of that subview.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell",forIndexPath: indexPath) as! UITableViewCell
if let nameLabel = cell.viewWithTag(100) as? UILabel{
var userAtIndexPath = finalMatchesBlurUser[indexPath.row]
nameLabel.text = userAtIndexPath.username.uppercaseString
}
else{
nameLabel = UILabel(frame: CGRectMake(cell.frame.size.width * 0.040, cell.frame.size.height * 0.22, cell.frame.size.width * 0.735, cell.frame.size.height * 0.312))
nameLabel.tag = 100;
var userAtIndexPath = finalMatchesBlurUser[indexPath.row]
nameLabel.text = userAtIndexPath.username.uppercaseString
cell.addSubview(nameLabel)
}
return cell;
}
the UILabel is created every time even when the cell is reused.
A solution is to create the UILabel in Interface Builder and assign a tag (e.g. 100).
Then use this code
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell",forIndexPath: indexPath) as! UITableViewCell
let nameLabel = cell.viewWithTag(100) as! UILabel
let userAtIndexPath = finalMatchesBlurUser[indexPath.row]
nameLabel.text = userAtIndexPath.username.uppercaseString
}