How to get didselectrowat from a custom cell with textboxes? - swift

I have a dynamic tableview with 3(cell1,cell2,cell3) custom cells, which gets reused anywhere from 0 times up to 9 times. (Maximum cells in the tableview will be 27 cells).
Each of those 3 custom cells have 3 text boxes each, and in addition the second cell has 2 buttons to select gender and 3rd custom cell has a drop down menu, which has values of whatever typed in the first textbox of cell1 cells.
Now the issue is that, textbox takes the touch input from user (as it should) and didselectrowat never gets called and therefore i am not getting indexPath.
But i need the indexPath, so that i can insert user details from each of those cells into an array.
(anArray.insert("", at: indexPath)
Since i am not getting indexPath, everything fails. How can i get the indexPath and get the touch on to the textbox?
I am thinking of something like a function that takes the touch ,gets the indexPath and then passes the control to textbox.
Things i have tried.
I disabled the textboxes before user touches the cell (and makes textboxes disabled once user deselects the cell, so cell takes the touch. Once didselectrowat gets called, make the textbox active again and make
textbox.becomeFirstResponder()
But somehow , the textbox never becomes the first responder even though breakpoint shows the control going through it and the textbox becomes active. But no keyboard popsup, if i touch the cell again, same process happens.
What seems to be the issue here? I saw another post on the same topic, i tried it but unfortunately that doesnt work and it was for a static tableview.
Also is there a good way to do this? Other than making the user click on the cell twice / clicking outside the textbox..

You can set your custom cell as delegate for UITextField and use a callback for editing begins/ends as below,
class MyCustomCell: UITableViewCell, UITextFieldDelegate {
public var editCallback: (() -> Void)?
/// Set 'textField.delegate = self' on initialization.
func textFieldDidBeginEditing(_ textField: UITextField) {
self.editCallback?()
}
}
And use the callback in cellForRowAt as,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = MyCustomCell()
cell.editCallback = { [weak self] in
print(indexPath)
}
return cell
}

you can try this
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
//your code here
}

Related

"Delay Touch Down" Property Seems to be Ignored on IOS <11

I have UITableView in a test project that is created this way:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
}
// height?
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//return cellHeightWithoutSeparator + cellSeparatorHeight
//print("YES!")
return 80 // arbitrary
}
// table wants a UITableViewCell for each row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
// customize cell here
return cell
}
// how many rows?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 50 // arbitrary
}
}
The structure of the table view is:
So, as you can see the cell contains a button.
I want the button (or any clickable view that is a subview of the cell) to respond immediately to the touch-down event and so I unchecked "Delay Touch Down" in the ScrollView property inspector in the storyboard.
It is my understanding that unchecking that box is equivalent to the code:
tableView.delaysContentTouches = false
According to the docs, this method is supposed to work on IOS 2.0+
But setting this property true or false only works as expected on IOS 11 and 12, but on IOS 10 (and presumably earlier), it is as if the box was never unchecked and touches on the button are still delayed.
I understand that it is still possible to make views "clickable" in the cell, and respond immediately to "touch up inside," (because the touch-up event will cancel the delay on the touch-down), but I still want the touch down event to call immediately (on all iOS versions, not just 11+) because:
1) I want the re-order control to respond immediately.
2) I want to have visual feedback for touch-down be normal/immediate on views in the cell.
I have also consulted this similar, but not the same, question which was written 4 years ago, and I tried applying the answers, but none of those answers seem to apply to whatever the modern day reason for this problem is.
Does anyone know the cause and (preferably Swift) solution to this?

Swift - Clicked button inside tableview cell does not call the did select row at function of that cell

I creating a tableview programatically and each tableview cell have buttons associated to them.
If I click the row I can work out the tag associated with the buttons on that row and then able to edit the title when applying some other functions.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
TmpActionConnectorTag = indexPath.row + 700 //Tag associated to button
}
When the button is clicked , I have this code which changes the other button on that row
let tag = TmpActionConnectorTag
let tmpButton = self.view.viewWithTag(tag) as? UIButton
The issue is if I directly click on the button in the tableview cell, the did select row does not get call and no tag value is given. To do it I have to click within the cell first and then the button to be able to know the tag associated with the row.
Is there a way to workout the index row when the button is clicked so I don't have to click the actual cell?
above is how the cell looks normally and below shows how the cell has to be selected to be able to get the index value.
Button action won't call the didSelectRowAt. You should go for delegate method. If not aware about delegate means refer this
The cell's button will not trigger func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) instead you need to add a target to your button which is typically done within cellForItemAt.
Add target to button within cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = MyTableViewCell()
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: #selector(didTapCellButton(sender:)), for: .touchUpInside)
}
Handle button action
#objc func didTapCellButton(sender: UIButton) {
guard viewModels.indices.contains(sender.tag) else { return } // check element exist in tableview datasource
//Configure selected button or update model
}
Disabling the button's control worked for me.
Programmatically:
editButton.isEnabled = false
~OR~
IB:
Uncheck the Enabled box in the Control section.

Better performance events inside TableView cell

I am developing a Facebook like application, where if the post is too long, it gets cut down and at the end of it "See more" appears that has some click events. (I use FRHyperLabel for it)
Everything works fine, but I've got a question: If the formatting+event is decleared inside tableView: cellForRowAt indexPath: is it bad for the performance?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var message : message
var cell : MessageCell
let message = self.messages[indexPath.row]
// World Message
cell = tableView.dequeueReusableCell(withIdentifier: "essageCell", for: indexPath) as! MessageCell
cell.messageLabel.attributedText = message.limitMessageMax250char()
//Add link if needed
if message.count > appSettings.maxCharacters.message {
let handler = {
(hyperLabel: FRHyperLabel!, range: NSRange!) -> Void in
self.alert(title: "Test", message: "Alert: because clicked on link")
}
let text = cell.messageLabel.text!
let nsRange = text.calculateUnicodeScalar(start: text.unicodeScalars.count-8, len: 8) // See more is 8 characters
cell.messageLabel.setLinkFor(nsRange, withLinkHandler: handler)
}
return cell
}
Or should I do it in the Cell's file, when it's awakeFromNib + with delegate
IMO you don't want to do this in the for cellForRowAt call mostly because you want to make that call return quickly and also because you are only returning the formatted cell.
I would argue/recommend that you set your handler for the row when it is being initialized and that way the handler value is only being set once.
Remember that cellForRowAt is called multiple times as the user scrolls through and as cells come into view.
review the https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614861-tableview for more information as to how cells are reused.

UITableViewController, cells are reloading incorrectly

On my app, once I tap to open all my cells they are all loaded on function by the tableView delegated method:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> MyClassName {...}
So every time the cells are loaded, this method is called.
By the first time it loads properly (all cells with black background)
When I go down with the screen and it loads some different cells it also follows my code correctly and loads the cells with white background.
The problem happens when I go back to the top.
Its like the number of white background cells are brought up, make the cells once black backgrounded to become white ones (and that's not what I wanted). I thought that once the cells are loaded in the screen, it should stay on device's memory.
I've put some prints on my code to check if they are incorrect but apparently they are working properly.
So I ask you, did any of you had this kind of problem or a similar one? Do you think this is an iOS bug?
EDITED, ADDED CODE:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> HistoricoSelectedCell {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "historicoCell", for: indexPath) as! HistoricoSelectedCell
let item = itemChosen[indexPath.row].item
let cell = self.configureCell(cell: cell2, indexPath: indexPath, item: item!)
return cell
}
It is a cell re-use issue.
You generate a new cell to use in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell by calling tableView.dequeueReusableCell(withIdentifier: <identifier string>, for: indexPath) I presume (you really need to post code with your question).
The cell that you get is reused, as in it is not instantiated from scratch but has already been created and used for other cells.
Your options are to:
1) Set all the necessary properties in the cellForRowAt indexPath method.
2) Create a fund configure(cell: UITableViewCell) function that sets the appropriate properties
3) Override prepareForReuse() in your UITableViewClass and restore your properties there.

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