I have a collection view with cells that are populated based on an array. Each cell has a UIImageView and an UILabel. I want to be able to update the text within the UILabel each time the Cell View itself is tapped. I have the tapping gesture working fine and i can print out the sender information and access the sender view and even get the restoration identifier but I can't seem to access the 'myImage' or 'myLabel' sub views within that cell which is being tapped.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! CardCell
cell.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap)))
let railroadName = railroadNames[indexPath.item]
cell.myImage.image = #imageLiteral(resourceName:railroadName)
cell.myImage.clipsToBounds = true
cell.myImage.layer.cornerRadius = 8
cell.restorationIdentifier = railroadName
cell.myLabel.isHidden = true
cell.myLabel.text = "2"
cell.myLabel.layer.backgroundColor = UIColor(red:0.25, green:0.52, blue:0.96, alpha:1.0).cgColor
cell.myLabel.layer.cornerRadius = 18
cell.myLabel.textColor = UIColor.white
return cell
}
#objc func tap(_ sender: UITapGestureRecognizer) {
//print(sender)
//print(sender.view?.restorationIdentifier)
}
Can't access subviews. Want to take the current text from 'myLabel' and increase the count by 1 each tap.
You can use below code to get label text of cell. but i insist you to manage this in railroadNames array. so, you can save label text in array object's field and can get that field value using indexpath using below code and perform operation on based of your requirement. and then you can reload that particular cell to show effect on tableview.
Also, you should use didSelectItemAt method to get select cell event. if not necessary reason to use UITapGestureRecognizer.
#objc func tap(_ sender: UITapGestureRecognizer) {
//Get Collection view cell indexpath on based of location of tap gesture
let cellPosition = sender.location(in: self.collectionView)
guard let indexPath = self.collectionView.indexPathForItem(at: cellPosition) else{
print("Indexpath not found")
return
}
//Get Ceollection view cell
guard let cell = self.collectionView.cellForItem(at: indexPath) as? CellMainCategoryList else{
print("Could't found cell")
return
}
//Get String of label of collection view cell
let strMyLable = cell. myLabel.text
//you can process addition on based of your requirement here by getting Int from above string.
}
instead of above, you can manage it like below code also:
#objc func tap(_ sender: UITapGestureRecognizer) {
let cellPosition = sender.location(in: self.collectionViewCategaries)
guard let indexPath = self.collectionViewCategaries.indexPathForItem(at: cellPosition) else{
print("Indexpath not found")
return
}
let railroadName = railroadNames[indexPath.item]
let myLableText = railroadName.myLabelValue
//update value
railroadName.myLabelValue = "2"
self.collectionView.reloadItems(at: [indexPath])
}
Hope this helps.
Related
In my view controller I have a table view in which I am loading multiple tableview cells. Some have UITextfield,Labels,Radio and check buttons. Now I have done all the part showing and entering data in tableview cell but not able to check whether any one field is left empty.
On button click in my controller I need to check this validation if all fields are non-empty. Or How can I get data from each field on button click.
Here is my cellForRowAt indexpath code that will give idea of diff cell I am loading. How can I validate if my uitextfield are empty and radio button are checked.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "StartEndDateCell", for: indexPath) as! StartEndDateCell
//cell with 2 textfield
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "NameTableViewCell", for: indexPath) as! NameTableViewCell
//cell with single textField
return cell
case 2:
let cell = tableView.dequeueReusableCell(withIdentifier: "StartEnddateTableViewCell", for: indexPath) as! StartEnddateTableViewCell
//cell open UIDATEPICKER here on click of textfield contains 2 textfield
return cell
case 3:
let cell = tableView.dequeueReusableCell(withIdentifier: "AmountTableViewCell", for: indexPath) as! AmountTableViewCell
//cell with single textfield with numeric keyboard
return cell
case 4:
let cell = tableView.dequeueReusableCell(withIdentifier: "MaxFixTableViewCell", for: indexPath) as! MaxFixTableViewCell
return cell
//cell with radio buttons
case 5:
let cell = tableView.dequeueReusableCell(withIdentifier: "infoTableViewCell", for: indexPath) as! infoTableViewCell
//for oneTimeLabel
let tapGestureFrequency : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(freqLblClick(tapGestureFrequency:)))
tapGestureFrequency.delegate = self as? UIGestureRecognizerDelegate
tapGestureFrequency.numberOfTapsRequired = 1
cell.oneTimeLabel.isUserInteractionEnabled = true
cell.oneTimeLabel.tag = 1
cell.oneTimeLabel.addGestureRecognizer(tapGestureFrequency)
//for onLabel
let tapGestureOnDebit : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(lblClickDebit(tapGestureOnDebit:)))
tapGestureOnDebit.delegate = self as? UIGestureRecognizerDelegate
tapGestureOnDebit.numberOfTapsRequired = 1
cell.onLabel.isUserInteractionEnabled = true
cell.onLabel.tag = 2
cell.onLabel.addGestureRecognizer(tapGestureOnDebit)
return cell
case 6:
let cell = tableView.dequeueReusableCell(withIdentifier: "RemarksTableViewCell", for: indexPath) as! RemarksTableViewCell
return cell
case 7:
let cell = tableView.dequeueReusableCell(withIdentifier: "InformTableViewCell", for: indexPath) as! InformTableViewCell
return cell
default:
let cell = tableView.dequeueReusableCell(withIdentifier: "checkTableViewCell", for: indexPath) as! checkTableViewCell
return cell
}
}
I think you can implement UITextFieldDelegate to track and update your data, for UITableViewCells with UITextField. But this requires you to write implement text delegate in each of the cell where you need to update your data set. Additionally for switch controls and check boxes you can monitor their valueChanged event.
For example
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let text = textField.text, let textRange = Range(range, in: text) {
let updatedText = text.replacingCharacters(in: textRange, with: string)
UserData.shared.name = updatedText
}
return true
}
#IBAction func switchValueChanged(_ sender: Any) {
UserData.shared.setting = switchControl.isOn
}
You will need to store this data somewhere, specially because UITableView reuse its cells, so you might lose what was inputed if the user scrolls.
Assuming you have an array where the the info for each cell comes from, you can create a flag that explicits if the user has answered that question. On cellForRowAtindexPath you'll check if the field is empty and reload the answer/state.
After that, you can create a validationMethod that should be called after every cell input, based on your local array of fields + answers.
all other validation like numeric type and date type i have done in cell class. I know it is not the proper way to do it. We can save our values to model class and use model class instead of doing it. doing in this way may reult in failure of validation when we scroll our tableview, but for now it is working in my code. Still I am waiting to do it in a write way. I will not mark my question as answered
at button click
#IBAction func nextButtonClicked(_ sender: Any) {
let nameCell = self.tableView.cellForRow(at: IndexPath(row: 1, section: 0)) as? NameTableViewCell
if (nameCell?.nameTextField.text!.isEmpty)! {
Log.i("TextField is Empty---" )
}
let startEndDateCell = self.tableView.cellForRow(at: IndexPath(row : 2,section : 0)) as? StartEnddateTableViewCell
if((startEndDateCell?.startDateTextField.text!.isEmpty)! || (startEndDateCell?.endDateTextField.text!.isEmpty)!){
Log.i("Start And End Date Empty")
}
let amountCell = self.tableView.cellForRow(at: IndexPath(row : 3, section : 0))as? AmountTableViewCell
if (amountCell?.amountTextField.text!.isEmpty)! {
Log.i("Amount Cell Empty---")
}
else{
let vc = NextViewController(nibName: "NextViewController", bundle: nil)
navigationController?.pushViewController(vc, animated: true )
}
}
I am trying to work out how to action buttons within a UITableViewCell when the table view is 'top down' - as in, each new row is added to the top of the table view. I achieve this with the following code:
I use the following code to insert new items into my model array and then into the tableview:
let newItem = Item(text: inputTextView.text, time: Date())
items.insert(newItem, at: 0) //inserting into the front of model array
tableView.beginUpdates()
let indexPath:IndexPath = IndexPath(row:0, section:0)
tableView.insertRows(at: [indexPath], with: .fade)
tableView.endUpdates()
Within the cellForRowAt function I run the following code:
let cell = tableView.dequeueReusableCell(withIdentifier: postCellID, for: indexPath) as! NewPostCell
let item = items[indexPath.row]
cell.postTextLabel.text = text
cell.timeLabel.text = dateFormatter.string(from: time)
cell.selectionStyle = .none
return cell
Each of my tableview cells have three buttons in it.
How do I connect up these buttons so I know which button is pressed from which indexPath?
The problem is that if I use indexPath.row to tag the buttons, then the buttons in all cells gets tagged with 0, as each insert is happening at the top of the table at indexPath.row 0th position.
I thought of tagging the buttons with the current size of my model array, but that doesn't work either as when cells are re-used they could then be tagged with the length of the array at that point, which would be wrong.
There are a lot of apps that have 'last entry at the top' of the tableview sort of setup, with buttons in cells. So there must be a way to do this.
You can add UIButton to your UITableViewCell and access these UIButton via tag and add target method to these buttons as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableVw.dequeueReusableCell(withIdentifier: "Cell") as UITableViewCell!
//Access UIButton
let button1:UIButton = cell.viewWithTag(10) as! UIButton
let button2:UIButton = cell.viewWithTag(11) as! UIButton
let button3:UIButton = cell.viewWithTag(12) as! UIButton
//Add Action Methods to UIButtons
button1.addTarget(self, action: #selector(FisrtButtonClick), for: .touchUpInside)
button2.addTarget(self, action: #selector(SecondButtonClick), for: .touchUpInside)
button3.addTarget(self, action: #selector(ThirdButtonClick), for: .touchUpInside)
return cell
}
Button Actions
// MARK: - UIButton Methods.
func FisrtButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
func SecondButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
func ThirdButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
Main idea
These are the basic steps you have to do:
Set unique tag values for each of the 3 button types: this will allow to identify which of the 3 buttons of a cell is pressed. (for instance tag value 1 for the first of your buttons, value 2 for the second kind, value 3 for the third button).
Link the 3 buttons to a #IBAction method.
You can also do this programmatically with target-actions: call aButton.addTarget(target:, action:, for:) for each button.
Then when a button will be pressed, you will use an helper function to determine the cell index of the button that was pressed.
Code
The #IBAction method should look like #IBAction func buttonTrigerred(_ sender: UIButton){...} and the code would be:
#IBAction func buttonTrigerred(_ sender: UIButton){
// -- retrieving the index path of the cell, as #NikhleshBagdiya posted
// Determine the indexPath of the cell
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableView)
let cellIndexPath = tableView.indexPathForRow(at: buttonPosition)
// --
// Determine which button is called
if sender.tag == 1 { ... } // the first kind of button has been pressed
else if sender.tag == 2 { ... } // second button kind
else if sender.tag == 3 { ... } // third button kind
}
Setting the tag value
Each of my tableview cells have three buttons in it.
I suppose you have designed your UITableViewCell in a Storyboard or Xib file. So for each of the 3 buttons of your cell: select the button, go to the attributes inspector, set the tag to the custom values indicated above.
The table view and cells have been created and populated with a label and slider.I have an IBAction on the slider when the value is changed.
How do I get that value back into the label in the cell?
I have an IBOutlet linked in the table view.
#IBAction func slideChange(_ sender: UISlider) {
let currentValue = sender.value // gets slider's value
let row = sender.tag // gets slider's row in table
myTableView.cell.myLabel.text = sender.value // Here i need to show updated value
print("Slider in row \(row) has a value of \(currentValue)") // works
}
Try this:
#IBAction func slideChange(_ sender: UISlider){
let cell: UITableViewCell? = (sender.superview?.superview as? UITableViewCell) // track cell using your view hierarchy and this your cell
cell.myLabel.text = sender.value
}
OR
let rootViewPoint: CGPoint? = sender.superview?.convert(sender.center, to: tblView)
var indexPath: IndexPath? = tblView?.indexPathForRow(at: rootViewPoint!)
let cell: UITableViewCell? = tblView?.cellForRow(at: indexPath!)
Get a reference to the table view cell
let cell = tableView.cellForRow(at: IndexPath(row: sender.tag, section: 0))
Update its label
cell?.myLabel.text = sender.value
Note: This method returns nil when cell is not visible. In your case this won't be an issue.
I have a tableview. i need to get the label "usernameLabel" from that cell and assign a variable to them. I need to pass that variable to prepareForSegue. The problem is the label is the wrong label from the wrong cell.
here is what i have:
var username: String!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : MainCell! = tableView.dequeueReusableCellWithIdentifier("MainCell") as! MainCell
username = usernameLabel.text
cell.button.userInteractionEnabled = true
let tapButton = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapLabel(_:)))
cell.button.addGestureRecognizer(tapButton)
return cell as MainCell
}
func tapButton(sender:UITapGestureRecognizer) {
performSegueWithIdentifier("ViewToView2Segue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ViewToView2Segue" {
let userProfileViewController = segue.destinationViewController as! SecondViewController
secondViewController.usernamePassed = usernamePassed
}
}
simplified question: i need to pass the label.text to another view controller via segue. but currently, it is getting the label from the wrong cell.
cellForRowAtIndexPath method will be multiple times so assigning value in that method will not work, also why are you using tapGesture on button instead of adding action to button, try to change your cellForRowAtIndex like this.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell : MainCell! = tableView.dequeueReusableCellWithIdentifier("MainCell") as! MainCell
cell.button.setTitle( usernameLabel.text, forState: .Normal)
cell.button.addTarget(self, action: #selector(self.buttonAction(_:)), forControlEvents: .TouchUpInside)
return cell
}
Now add this buttonAction function inside your ViewController
func buttonAction(sender: UIButton) {
let center = sender.center
let point = sender.superview!.convertPoint(center, toView:self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)
let cell = self.tableView.cellForRowAtIndexPath(indexPath) as! MainCell //Add superview on the basis of your button hierarchy in the cell
username = cell.usernameLabel.text
print(username)
performSegueWithIdentifier("ViewToView2Segue", sender: self)
}
You should not save state data in cells. You should have the info that you want in your model (probably an array). When the user taps a cell you should use the indexPath of the selected cell to fetch the info from the model, not from a label on the cell.
(Look up the MVC design pattern for background. A table view cell is a view object, and should not store data. That's the model's job.)
I am developing app which users will choose one of the two pictures in one cell. My prototype cell looks like :
How can I detect when the user presses the vote button which cell is selected ?
My tableView Code :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "NewTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NewTableViewCell
//For left image
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].poll_photo1 ){
cell.leftImage.sd_setImageWithURL(url)
}
//For right image
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].poll_photo2 ){
cell.rightImage.sd_setImageWithURL(url)
}
//For user picture
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].users[0].user_photo ){
cell.userPicture.sd_setImageWithURL(url)
}
// gets username and text
cell.userName.text=self.polls[indexPath.row].users[0].user_name
cell.description.text = self.polls[indexPath.row].poll_textfield
return cell
}
My web API:
I am assuming that your custom table view cell NewTableViewCell is having an outlet for your vote button.
Just tag your voteButton with indexPath.row and fetch the tag in its target function as shown below. You will get to know which cell's voteButton was tapped when you press your Vote Button
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("NewTableViewCell") as! NewTableViewCell
//Tagging with indexPath.row
cell.voteLeftButton.tag = indexPath.row
cell.voteRightButton.tag = indexPath.row
//This is the latest Swift 2.2 syntax for selector. If you are using the older version of Swift, Kindly check the selector syntax and make changes accordingly
cell.voteLeftButton.addTarget(self, action: #selector(voteLeftButtonPressed), forControlEvents: .TouchUpInside)
cell.voteRightButton.addTarget(self, action: #selector(voteRightButtonPressed), forControlEvents: .TouchUpInside)
return cell
}
func voteLeftButtonPressed(sender:UIButton){
print("Left Button Table cell clicked is \(sender.tag)")
}
func voteRightButtonPressed(sender:UIButton){
print("Right Button Table cell clicked is \(sender.tag)")
}
Add target/action to your button in cell configure method like this:
let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourController.tapAction(_:)))
Then implement tapAction method
func tapAction(sender : UITapGestureRecognizer)
{
if sender.state != UIGestureRecognizerState.Ended
{
return
}
let btn = sender.view as! UIButton
let pointTo : CGPoint = CGRectOffset(btn.bounds, btn.frame.size.width/2, btn.frame.size.height/2).origin;
let buttonPosition : CGPoint = btn.convertPoint(pointTo, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)
...
}