UITableViewCell's attributed text not updating - swift

I have a table view that is connected through a Core Data's NSFetchedResultsControler. The cells have a UILabel with attributed text and a UISwitch representing the object's isEnabled value. Inside the cell, I have a function:
func configure(with viewModel: CellViewModel) {
myCustomLabel.attributedText = viewModel.attributedText // NSMutableAttributedText
}
Chaning the switch changes the isEnabled value on the object, which in cause raises the NSFetchedResultsControler, which calls:
let cell: MyTableViewCell = tableView.dequeueReusableCell(forIndexPath: indexPath) //my custom helper method
let cellViewModel = viewModel.itemModel(at: indexPath)
cell.configure(with: cellViewModel)
I do not want to use reloadData, reloadRows or reloadSections, because I want to keep animations while changing the cell's state and avoid cell fading animation if only switch is flipped. I know this is quite bad approach but it's required to keep the animations.
The problem is: The attributedText doesn't change on the label immediately. Only after cell is reused. The debugger displays the new value of the label correctly. I have tried:
this suggestion (setting font and color to nil beforehand),
calling setNeedDisplay on both the label and it's superview
setting the non-attributed text first, then attributedText
Nothing works. Do you have any ideas why it's like that?
System is iOS 11.4, XCode 9.4.1.

If you wrap myCustomLabel.attributedText = viewModel.attributedText in a DispatchQueue.main.async { } it will work:
DispatchQueue.main.async {
myCustomLabel.attributedText = viewModel.attributedText
}

I think that instead of:
let cell: MyTableViewCell = tableView.dequeueReusableCell(forIndexPath: indexPath)
You should use:
tableView.cellForRow(at: indexPath)
That will return you the cell instance for that indexPath. By calling "dequeueReusableCell" you are essentially saying you want a new Cell.
That assumes that I get what you are trying to achieve and your snippet is not from the
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
method.
If that doesn't work might be worth adding a git link to take a look at your code.
Hope that helps!

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

I have a UItableView cell with a Image but can't get a image click to allow parameters

I have a EDIT2 UItableView, in Swift, with the normal labels and relevant Information. I have added a Image to the cell and all is working except when I click on the image I would like to open another ViewController with that particular cells relevant information. I have used:
let popup: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.popupalrmcontrnt))
cell.alrmwarning.addGestureRecognizer(popup);
as the means for calling the pushViewController function, But the Viewcontoller requires a parameter AlarmID to be set Beforehand. Said Viewcontroller needs AlarmID to fetch its relevant information.
Edit1 The parameter is dependant on the selected cell.
Create a closure variable in cell:
class SomeCell: UITableViewCell {
var imageClicked: (() -> Void)?
}
then in action of your tap gesture self.popupalrmcontrnt
add this line
func popupalrmcontrnt() {
self.imageClicked?()
}
then in your cellForRow, you can access this as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
(cell as? SomeCell)?.imageClicked = {
//Navigate to your view controller here
}
return cell
}
Hope it helps!!
Better use UIButton. you can set image to the button as background image. Also you can pass relevant info as an object to the new ViewController.

Add switch in UITableView cell in Swift

How can I embed a UISwitch programmatically in a tableView cell in Swift?
I'm doing it like that
let shareLocationSwitch = UISwitch()
cell.accessoryView = shareLocationSwitch
Here is way you can embed a UISwitch on a UITableView cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "yourcellIdentifire", for: indexPath) as! YourCellClass
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
return cell
}
here is switch call beck method
func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
}
#LeoDabus Great! explanation.
Note: if your tableview may have more than one section then You should create a CustomCell subclassing UITableViewCell and configure your accessoryView inside UITableViewCell awakeFromNib method instead of table view cellForRowAt method. When dequeuing the reusable cell cast it to your CustomCell Here is sample from #LeoDabus
if you update the array with 3 elements and refreshing the table using self.tableView.reloadData() (for example after 2 second) you can see that with Xcode13 the switch values will be swapped and the example doesn't work. I have similar issue with my App when Xcode has been released to 13. try this: https://drive.google.com/file/d/1_1HuI_JFIYusNmsdyekGBWXsGznneiQ7/view?usp=sharing

didSelectCellAtIndexPath not called

I know there are a lot of relevant post on this and I have tried all of the ones I can find and unfortunately the problem still persists.
So I have a tableView inside of an UIViewController which is populated by 2 custom cells, one has got an UITextField and the other one got an UISwitch, and both have a UILabel. I then proceeded to implement the didSelectCell(at: IndexPath) method
I made sure the delegate is hooked up to the view controller.
Inside of the method, I simply wrote a print statement to ensure the taps are registering. However, the print message did to get printed to the console when I tapped on the UITextField and on the UILabel.
I have a feeling it is something to do with the firstResponder thing with the UITextField, and it is eating away my tap registration, but not really sure.
Any feedbacks are welcome!
UPDATE
Here is the setup on my custom cell:
Here is the didSelect method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("tapped on cell")
if let cell = tableView.cellForRow(at: indexPath) as? NumericInputTableViewCell{
print("Tapped on numericcell")
} else if let cell = tableView.cellForRow(at: indexPath) as? BooleanInputTableViewCell {
print("Tapped on booleancell")
}
}
The delegate method is triggered when you click on the cell, not other elements on the cell. So create some empty cell in your table view and have another try. If it get printed out then that means you have elements covered over the cell and the cell itself is not clicked.
You can then disable user interaction for your UITextField stuffs from storyboard and then have a try. Ideally, when you click on the textfield on a table view cell, the correct respond I am thinking is that keyboard will show up and didSelectCell should not be triggered.
Here is the demo

Retrieving Information from Specific UICell Using Swift

In my program, I have an UITableView and I need to access a label in one of the UITableViewCells. I have the indexPath for the UITableViewCell, but I was wondering how I should proceed in grabbing the cell's label using Swift. Any ideas on how you access a cell without being in the table view swift file?
You shouldn't go through the cell to grab the content of the label. Instead, you should go through whatever data structures that you used to set that label in the first place.
Go to your tableView:cellForRowAtIndexPath: code, and find the part where you set up the label in the cell
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell
cell.textLabel?.text = self.items[indexPath.row] // <<== HERE
return cell
}
You have code similar to the above, where you use the source of data to set up your label from an indexPath. Using the same code to obtain the text that went into your label provides the most direct way of getting the data.