I have a UITableView with my own UITableViewCells, which contains a UIButton. The UITableView is added to my ViewController at runtime. Everything works fine that way that I see the Button in the Cells. When pressing the Button my action is called in my UITableViewCell.
Now. How can I call a method in my ViewController, when the button is pressed in my cell ?
In my UITableViewCell Class I have a button in a cell:
let framebutton_in_cell:CGRect=CGRectMake(0, 0, 40,40);
button_in_cell.frame=framebutton_in_cell;
button_in_cell.addTarget(self, action: "buttonActionText:", forControlEvents: UIControlEvents.TouchUpInside)
button_in_cell.tag = 1;
var image2 = UIImage(named: "book");
button_in_cell.setImage(image2, forState: .Normal)
var transparent2:UIColor=UIColor(red: 0, green: 0, blue: 0, alpha: 0.0);
contentView.addSubview(button_in_cell);
and I have my function buttonActionText:
func buttonActionTimeline(sender:UIButton!)
{
var btnsendtag:UIButton = sender
//do something
}
My UITableView is added at runtime to my UIViewController:
override func viewDidLoad() {
super.viewDidLoad()
let fSearchResultY:CGFloat=fButtonHeight+fButtonY;
searchResults.frame = CGRectMake(0, fSearchResultY, screenwidth, screenheight-fSearchResultY);
searchResults.delegate = self;
searchResults.dataSource = self;
searchResults.registerClass(CellResult.self, forCellReuseIdentifier: "Cell");
self.view.addSubview(searchResults);
Now I would like to call a method in my Viewcontroller insteed of a method in my UITableViewCell. How can I achieve this ?
Just don't add target from UITableViewCell class instead add it in cellForRowAtIndexPath method
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cellIdentifier", forIndexPath: indexPath) as! SomeCustomCell
cell.button_in_cell.addTarget(self, action: "viewControllerMethod:", forControlEvents: UIControlEvents.TouchUpInside)
}
A very simple solution :-
Just declare delegate method in your Custom cell.
Set the delegate of Cell with the View Controller cell.delegate=self;
Now call the delegate method on Cell's button action and also send the button.tag as object(if needed) , it will directly call your View Controller's delegate method which is defined .
I didn't got your question properly ?Is this u r looking for
func buttonActionText(sender:AnyObject){
CGPoint buttonPosition=sender.convertPoint(CGPointZero, fromView: self.tableView)
NSIndexPath indexPath = [self.tableView indexPathForRowAtPoint:buttonPosition];
}
Create a subclass of UITableviewCell. In my code i have used Question Cell
func buttonActionTimeline(sender: UIButton!)
{
var btn:UIButton=sender as UIButton
let point = tblVIew.convertPoint(CGPointZero, fromView: sender)
let indexPath : NSIndexPath = tblVIew.indexPathForRowAtPoint(point)!
let cell = tblVIew.cellForRowAtIndexPath(indexPath) as QuestionCell // Your custom cell class name
cell.loaddata() // add this method in to custom cell class
}
Related
i have a table view which has multiple section
i want to know the indexPath when user tapped on itemImage.
class ItemCell:UITableViewCell{
#IBOutlet weak var itemImage:UIImageView!
#IBOutlet weak var itemName:UILabel!
}
In your cellForRow, add click action to your UIImageView and pass the tapGestureRecognizer, which is used to get the tapped image view in click handler function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell") as! ItemCell
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
cell.itemImage.isUserInteractionEnabled = true
cell.itemImage.addGestureRecognizer(tapGestureRecognizer)
return cell
}
Then in your handler function, you can get your image view. So your image view is located in the content view, which is then located in the cell. So you can get the cell by calling parent, and then further get the indexPath
func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) {
let tappedImage = tapGestureRecognizer.view as! UIImageView
if let cell = tappedImage.superview?.superview as? ItemCell{
let indexPath = self.YourTableView.indexPath(for: cell)
}
//... other code here
}
pass your ItemImage and TableView instance in this function
func viewIndexPath(subView:UIView , tableView:UITableView)->IndexPath?{
let subViewPosition = subView.convert(subView.frame ,to:tableView)
if let indexPath = tableView.indexPath(at:subViewPosition){
return indexPath
}
return nil
}
What you want to do is add a delegate to your cell and create a protocol inside the cell like so
protocol ItemCellProtocol {
func didSelectCell(cell: ItemCell)
}
Your viewController with the tableView inside it should confirm to this protocol and implement this method.
Add
var delegate: ItemCellProtocol?
To ItemCell
Everytime you dequeue a ItemCell set the delegate property to self.
Then inside your cell add a method for listening for the imageTapped
func imageTapped(tapGestureRecognizer: UITapGestureRecognizer) { {
delegate?.didSelectCell(self)
}
This will fire your method in the viewController and pass a reference of an item cell to it inside that method just call indexPathForCell method on the table view and you'll have the indexPath
I've googled this answer multiple times and what I'm finding is not either working or doesn't make sense to me. Could anyone take a whack at this and help me out? Been stuck on this issue for hours.
Goto to CellForRowAtIndexPath in UITableView Delegate Method
Add this code it works for you
For UILabel:
var newLabel = UILabel(frame: CGRectMake(280.0, 14.0, 300.0, 30.0)) //make frame according to your need
newLabel.text = newData[indexPath.row]
newLabel.tag = 1
for UISwitch:
var switchView: UISwitch = UISwitch(frame: CGRectZero)
aCell.accessoryView = switchView
switchView.setOn(false, animated: false)
switchView.addTarget(self, action: "switchChanged:", forControlEvents: .ValueChanged)
What do you mean by not working? Just drag a tableview in the storyboard's view controller and then a UITableViewCell into it, followed by a UILabel and UISwitch. I've attached a screenshot.
Create a tableviewcell custom class, drag and link the switch and label into .h of the class file and that's it you'll get it working as simple as that.
Don't forget to link UITableview's 'datasource' and 'delegate' method's.
You can download custom cell with table view example.
There is label and switch.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell : CustomCell! = tableView.dequeueReusableCellWithIdentifier("ID_CustomCell", forIndexPath: indexPath) as! CustomCell
cell.selectionStyle = UITableViewCellSelectionStyle.None
cell.lblData.text = myArray.objectAtIndex(indexPath.row).valueForKey("Data") as? String
if let str = myArray.objectAtIndex(indexPath.row).valueForKey("Data") as? String
{
cell.lblData.text = str as String
}
cell.switch1.tag = indexPath.row
cell.switch1.addTarget(self, action: "switchChanged:", forControlEvents: UIControlEvents.ValueChanged)
return cell
}
func switchChanged(mySwitch: UISwitch) {
let no = mySwitch.tag
let selectedIndex = NSIndexPath(forRow: no, inSection: 0)
let currentCell : CustomCell = table1.cellForRowAtIndexPath(selectedIndex)! as! CustomCell
if currentCell.switch1.on
{
currentCell.switch1.setOn(true, animated: true)
}
else
{
currentCell.switch1.setOn(false, animated: true)
}
}
In switchChanged method you can change state of you array. so you can utilize that array later on.
All the best.
Update
Just updated downloadable code with cellForRowAtIndexPath and switchChanged.
I'm trying to change the color of a button when pressed. Currently its inside a table view cell.
The way I'm doing it is adding as so:
#IBAction func upVote(sender: AnyObject) {
sender.setImage(UIImage(named: "bUpVote"), forState: .Normal)
}
and this is done inside the cell class (not the view controller class).
It works, but the change also applies to every third cell that follows it for the rest of the table.
Any work around? Thanks!
There are many way to solve this issue, one of the method is as follows
Add this to your customCell class,
#objc protocol MyTableViewCellDelegate {
func controller(controller: MyTableViewCell, button: UIButton, selectedButtonIndexPath : NSIndexPath)
}
class MyTableViewCell: UITableViewCell {
var delegate: AnyObject?
var indexPath : NSIndexPath?
#IBOutlet weak var button: UIButton!//outlet of button
button Action
#IBAction func buttonAction(sender: UIButton)//IF the sender type is AnyObject, you have to change it as UIButton
{
self.delegate?.controller(self, button: sender, selectedButtonIndexPath: indexPath!)
}
Add this to your ViewController class that has UITableView
class MyTableViewController: UITableViewController, MyTableViewCellDelegate { // I created a subClass of UITableViewController, your's may be different
var arraySelectedButtonIndex : NSMutableArray = []//global declaration
Since i created my custom cell using xib, in viewDidLoad()
tableView.registerNib(UINib(nibName: "MyTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomCell")//Since, I use custom cell in xib
define delegate of custom cell by adding this
func controller(controller: MyTableViewCell, button: UIButton, selectedButtonIndexPath : NSIndexPath)
{
if(arraySelectedButtonIndex .containsObject(selectedButtonIndexPath)==false)
{
arraySelectedButtonIndex.addObject(selectedButtonIndexPath)
button.setImage(UIImage(named: "bUpVote") , forState: .Normal)
}
else
{
arraySelectedButtonIndex.removeObject(selectedButtonIndexPath)//If you need to set Deselect image
button.setImage(UIImage(named: "deselectImage") , forState: .Normal)//If you need to set Deselect image
}
}
In tableView dataSource (cellForRowAtIndexPath)
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! MyTableViewCell
cell.delegate = self
cell.indexPath = indexPath
if(arraySelectedButtonIndex .containsObject(indexPath))
{
cell.button.setImage(UIImage(named: "bUpVote"), forState: .Normal)
}
else
{
cell.button.setImage(UIImage(named: "deselectImage"), forState: .Normal)//If you need to set Deselect image
}
return cell
}
It's because cells are reused by the tableView. if you need to persist the state of subviews in the cell, you need to update your data source and reflect the changes in cellForRowAtIndexPath method.
This is not the way to do it. You store the state of the button in your model. Eg: say store the item's upvoted status in your model :
class Post
{
var title : String
var upvoted : Bool
}
How to get the index path ?
Move the IBAction method on your custom tableview subclass. Add a property called delegate to the cell and set it to your controller in cellForRowAtIndexPath: . Now in the action method inform the delegate.
I have described this in detail here : https://stackoverflow.com/a/32250043/1616513
Now when the user upvotes you update the model :
#IBAction func upVotedInCell(sender: UITableViewCell) {
var indexPath = self.tableView.indexPathForCell(sender)
self.items[indexPath].upvoted = true
self.tableView.reloadRowsAtIndexPaths([indexPath],UITableViewRowAnimation.None)
}
I have a UITableView with multiple sections and in my cellForRowAtIndexPath method I add a UITapGestureRecognizer to the cell's imageView (each cell has a small image). I have been successful in accessing that image (in order to change it) by using:
var imageView : UIImageView! = sender.view! as UIImageView
What I need to do now, however, is access the cell's data, which I believe means I need to be able to access the cell in order to get the section and row number. Can anyone advise on how to do this? The idea is that I am changing the image to an unchecked checkbox if the task is done, but then in my data I need to actually mark that task as done.
In your function that handles the tap gesture recognizer, use tableView's indexPathForRowAtPoint function to obtain and optional index path of your touch event like so:
func handleTap(sender: UITapGestureRecognizer) {
let touch = sender.locationInView(tableView)
if let indexPath = tableView.indexPathForRowAtPoint(touch) {
// Access the image or the cell at this index path
}
}
From here, you can call cellForRowAtIndexPath to get the cell or any of its content, as you now have the index path of the tapped cell.
You can get the position of the image tapped in order to find the indexPath and from there find the cell that has that image:
var position: CGPoint = sender.locationInView(self.tableView)
var indexPath: NSIndexPath = self.tableView.indexPathForRowAtPoint(position)!
var cell = self.tableView(tableView, cellForRowAtIndexPath: indexPath)
You're missing that you can use the Delegate design pattern here.
Create a protocol that tells delegates the state changes in your checkbox (imageView):
enum CheckboxState: Int {
case .Checked = 1
case .Unchecked = 0
}
protocol MyTableViewCellDelegate {
func tableViewCell(tableViewCell: MyTableViewCell, didChangeCheckboxToState state: CheckboxState)
}
And assuming you have a UITableViewCell subclass:
class MyTableViewCell: UITableViewCell {
var delegate: MyTableViewCellDelegate?
func viewDidLoad() {
// Other setup here...
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "didTapImage:")
imageView.addGestureRecognizer(tapGestureRecognizer)
imageView.userInteractionEnabled = true
}
func didTapImage(sender: AnyObject) {
// Set checked/unchecked state here
var newState: CheckboxState = .Checked // depends on whether the image shows checked or unchecked
// You pass in self as the tableViewCell so we can access it in the delegate method
delegate?.tableViewCell(self, didChangeCheckboxToState: newState)
}
}
And in your UITableViewController subclass:
class MyTableViewController: UITableViewController, MyTableViewCellDelegate {
// Other methods here (viewDidLoad, viewDidAppear, etc)...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) {
var cell = tableView.dequeueReusableCellWithIdentifier("YourIdentifier", forIndexPath: indexPath) as MyTableViewCell
// Other cell setup here...
// Assign this view controller as our MyTableViewCellDelegate
cell.delegate = self
return cell
}
override func tableViewCell(tableViewCell: MyTableViewCell, didChangeCheckboxToState state: CheckboxState) {
// You can now access your table view cell here as tableViewCell
}
}
Hope this helps!
Get tableView's cell location on a specific cell. call a function which holds the gesture of cell's Objects Like UIImage or etc.
let location = gesture.location(in: tableView)
let indexPath = tableView.indexPathForRow(at: location)
Print(indexPath.row)
Print(indexPath.section)
I have a custom UITableViewCell subclass and its associated xib. I have a UILabel and a UIButton in this cell and I have wired the touch up inside action of the button to the subclass.
What I need is when that button in the cell is tapped, to get the indexpath of the cell which has that button. And maybe send it back to the view controller via a delegate or something.
Since I'm inside a UITableViewCell subclass, I can't use a solution like this because I don't have a reference to the tableview from inside the cell subclass. Upon further investigation I found another solution and I implemented it in Swift like this.
import UIKit
class ContactCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
selectionStyle = .None
}
#IBAction func callButtonPressed(sender: UIButton) {
let indexPath = (self.superview as UITableView).indexPathForCell(self)
println("indexPath?.row")
}
}
But when I tap on the button, it crashes with an error message saying Swift dynamic cast failed.
Any idea what's wrong with my code?
Or I'm open to any other suggestions which would allow me to achieve the desired result in any other way.
Thank you.
Sounds like you need a delegate:
Delegates in swift?
Then just pass the cell itself as a parameter to the delegate, and then you can easily do tableView.indexPathForCell(cellFromDelegateMethod)
Hey you can use "Tag" of the button also.Inside the cellForRowAt method of table delegate u can tag the button with Indexpath.row . here is the example what i m tried to say.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// get ur cell nib .As it has a button
cell.startOrConntinuBtn.addTarget(self, action: #selector(sumbitOrContinue), for: .touchUpInside)
cell.startOrConntinuBtn.tag = indexPath.row }
and in the touch method "sumbitOrContinue" -
func sumbitOrContinue(sender: UIButton!) {
let tag = sender.tag
// do what you want to do like this example
let detail = self.detailList[tag]
let vc = self.storyboard?.instantiateViewController(withIdentifier: "mockExamInt") as! MockWindowVc
vc.detailId = detail.id
self.navigationController?.pushViewController(vc, animated: true)}
UIButton.Type really does not have member superview, but sender have
var cell: UITableViewCell = sender.superview.superview as UITableViewCell