Get TableViewController from TableViewCell - ios

I have a custom class for a table cell that is connected to a switch within the table cell (with an action) and I want to be able to to communicate to the TableViewController that the action happened as well as the path of the cell. The way that initially came to mind was if I could use some function in UITableViewCell to get the TableViewController of the table the cell is part of, as my custom class is (rather obviously) a subclass of UITableViewCell. Please tell me if I'm missing something.

To get a reference to the containing view controller, I add a weak property on the cell subclass.
#property (nonatomic, weak) UITableViewController *viewController;
You can assign this value in cellForRowAtIndexPath:

For accessing each cell in TableView
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as YourTableViewCell
cell.mainTextLabel.text = self.venueService.mainCategoriesArray()[indexPath.row]
return cell
}
For getting selected cell in TableView
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell;
print(currentCell)
}
Reaching from TableViewCell to TableView
self.superview //// self is TableViewCell and its superview represents TableViewController
hope that helps

You should make the ViewController the target of the switch action.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("SwitchCell", forIndexPath: indexPath) as! SwitchTableViewCell
cell.onSwitch.addTarget(self, action: Selector("cellSwitchDidChange:"), forControlEvents: .ValueChanged)
cell.label.text = "This is a Switch"
return cell
}
func cellSwitchDidChange(sender: UISwitch) {
let origin = sender.convertPoint(CGPointZero, toView: tableView)
let indexPath = tableView.indexPathForRowAtPoint(origin)!
// do something
}

Related

Swift | DynamicTableView - Trigger Action with ImageView

I have a DynamicTableView and every Cell has an ImageView. How do I make an Action with knowing the indexPath.row?
The only way I know is to make a TapGestureRecognizer – that works but I then don't know which row I clicked.
So I need something like DidSelectRowAt from the TableView:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
This Action should only happen when I press the ImageView not the whole Row, therefore I can't use DidSelectRowAt from the TableView.
Thats what I did right now:
class DownloadsViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tap = UITapGestureRecognizer(target: self, action: #selector(DownloadsViewController.tappedMe))
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DownloadsViewCell
cell.fileInfo.image = UIImage(named: "detail1")
cell.fileInfo.addGestureRecognizer(tap)
cell.fileInfo.isUserInteractionEnabled = true
}
func tappedMe(){
print(arrayDocuments(IndexPath.row))
}
}
My whole DownloadViewContoller Class Code is here
My DownloadViewCell Screenshot is here
Does anyone have an Idea?
Thank you!
Add a property to your cell: var indexPath: IndexPath.
In cellForRowAtIndexPath: do cell.indexPath = indexPath
In your cell subclass add tapGestureRecognizer to UIImageView which triggers method in cell subclass.
Make a delegate protocol for your cell:
protocol YourCellDelegate: class {
func imagePressed(indexPath: IndexPath)
}
Make your ViewController adopt this protocol.
In your cell subclass add property weak var delegate: YourCellDelegate?
In cellForRowAtIndexPath: do cell.delegate = self
In cell tapRecognizer selector call delegate method delegate?.imagePressed(indexPath: indexPath)
In your viewController you'll receive imagePressed method call with indexPath as an argument.
Just set the ImageView like you had before, and then just add a blank Button over it and use the method from the Answer from Ilya V. with the IBAction
Did you try to get cell by
recognizer.view
on tap gesture method and
func indexPath(for cell: UITableViewCell) -> IndexPath?
returns an index path representing the row and section of a given table-view cell.
Here is a solution that uses a custom UIImageView subclass.
Create a subclass of UIImageView - MyImageView (or something else more appropriate)
Create a delegate for the subclass - MyImageViewDelegate
Add a delegate method called myImageViewTapped(imageView: MyImageView)
Override the touchesEnded method to call the delegate method
Add a property in MyImageView called indexPath that stores the index path of the cell that this image view is in.
In your table cells, use MyImageView instead of a regular UIImageView.
Set the delegate of the MyImageView to self i.e the VC.
Set isUserInteractionEnabled to true
In cellForRowAtIndexPath, set the image view's indexPath to the indexPath parameter
Now implement the delegate method and in the method, you will know which image view is tapped by accessing indexPath!
You can use property "tag" imageView to save indexPath.row and use it later in the selector
class DownloadsViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DownloadsViewCell
cell.fileInfo.image = UIImage(named: "detail1")
cell.fileInfo.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(tappedMe(_ :)))
cell.fileInfo.addGestureRecognizer(tap)
}
#objc func tappedMe(_ sender: AnyObject){
print("Tap to imageView in cell No. - \(sender.view.tag)")
}
}

How to open a new screen when tapping on a UIImageView that is in a UITableViewCell class?

I have my TableViewCell inside it's own class called VersionCellVC.
I have my UIImageView completely covering up the cell; so the users have no choice but to tap on the UIImageView, not the TableViewCell.
I want to perform a segue or present a vc, and I have already created the gesture recognizer and the function it calls. however, since I am in a custom subclass of UITableViewCell, I am being told by the system that I am not allowed to use the present(vc:animate:complete) nor the prepare(forSegue:..:)) functions since they are not members of the UITableViewCell class.
How do I solve this without changing the UIImageView to a UIButton?
what about something like this (in your viewcontroller that contains the tableview)?
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.myImageView?.isUserInteractionEnabled = true
cell.myImageView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(imageViewTapped(imageView:))))
return cell
}
func imageViewTapped(imageView: UIImageView) {
if let indexPath = tableView.indexPathForRow(at: imageView.convert(.zero, to: tableView)) {
print("user tapped imageview at indexPath \(indexPath)")
}
}

How to know which IndexPath.row is clicked on IBAction, and how to exclude a specific button from it being triggered

I have a UITableViewController. It has one prototype cell, and spawns anywhere from 0 to 100 cells based on a user query. Once they load, I want an IBAction to trigger if a user taps anywhere within the cell EXCEPT for a specific button. I have multiple labels, and I still want the IBAction to be triggered if user taps on them. How do I accomplish this?
Here's my code for loading the tables:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TypeOfActivityTableViewCell
cell.activityLabel?.text = activityNames[indexPath.row]
cell.bgImage?.image = UIImage(named: activityPic[indexPath.row])
cell.sloganLabel?.text = slogan[indexPath.row]
return cell
}
I do not want to code all this within tableView for MVC principles.
For tapping anywhere in the cell you need not configure an IBAction. Just use the default UITableViewDelegate i.e:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
let geoSearchWord = "geoSearchWord" + searchQuery[indexPath.row]
let geoSearchLat = "&geoSearchWordLat=" + (lat == "" ? "33.9700" : lat)
let geoSearchLon = "&geoSearchWordLon=" + (lat == "" ? "-118.4180" : lon)
let geoSearchRadius = "&geoSearchWordRad=5mi"
let twitterURLRequest: String = "https://quiet-cove-5048.herokuapp.com/tw?\(geoSearchWord)\(geoSearchLat)\ (geoSearchLon)\(geoSearchRadius)"
alamoRequest(twitterURLRequest)
}
Else if you have Buttons inside your cell for specific actions. You will have to add action to that button and assign a tag for identification(which button is tapped).
You can add action in cellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TypeOfActivityTableViewCell
cell.activityLabel?.text = activityNames[indexPath.row]
cell.bgImage?.image = UIImage(named: activityPic[indexPath.row])
cell.sloganLabel?.text = slogan[indexPath.row]
cell.myButton.addTarget(self, action: "myAction:", forControlEvents: .TouchUpInside)
cell.myButton.tag = indexPath.row
return cell
}
Tapping on a button inside the cell will execute the below function:
func myAction(sender:UIButton){
let selectedActivityName = activityNames[sender.tag]
}
Here is one crafty solution.You can add an UIControl(e.g. UIButton) to receive touch in that special area.And then receive events in UITableViewDelegate: tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
If you want click any where in cell then you can use its delegate methods
i.e
tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
And if you want to add UIControl and on touch event of that control you need to do as follows:
First You need to add target for your control in your cellForRowAtIndexPath as below.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TypeOfActivityTableViewCell
cell.activityLabel?.text = activityNames[indexPath.row]
cell.bgImage?.image = UIImage(named: activityPic[indexPath.row])
cell.sloganLabel?.text = slogan[indexPath.row]
cell.btn.addTarget(self, action: "btnClickAction", forControlEvents: UIControlEvents.TouchDragInside)
return cell
}
Button Click fucntion:
func btnClickAction(sender: UIButton)
{
var superView = sender.superview
while superView?.isKindOfClass(UITableViewCell) == false
{
superView = superView?.superview
}
var cell = superView as! UITableViewCell
var indexPath = self.tableView.indexPathForCell(cell)! as NSIndexPath
}
In above code you will find indexPath.

Custom Cell Label

I have a custom cell that I'm designing.
I want to pass a string to that custom cell. I made a cocoa touch class called AlarmCell (subclass of UITableCell), and then called it with this code (in my UIViewController class):
let myCell = AlarmCell()
And then called the UILabel from within AlarmCell.
myCell.timeLabel?.text = newAlarm
newAlarm has the string "08:38 PM"
How come myCell.timelabel is coming out as nil? What am I doing wrong?
Edit:
AlarmCell Class:
class AlarmCell: UITableViewCell {
#IBOutlet var timeLabel: UILabel!
}
Alarm(UIViewController Class):
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let Cell = tableView.dequeueReusableCellWithIdentifier("alarmCell", forIndexPath: indexPath) as UITableViewCell
I had:
cell.textLabel?.text = alarms[indexPath.row]
But I need to pass a string to a custom labl inside the cell
Do yourself a favor and create a nib file associated with your AlarmCell.swift class. After customizing the cell inside the nib file, declare the following inside your viewDidLoad function.
var nib = UINib(nibName: "AlarmCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
Then, mention it in your cellForRowAtIndexPath function as so:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:AlarmCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as AlarmCell
cell.timeLabel.text = alarms[indexPath.row]
return cell
}
Try changing cell.textLabel?.text = alarms[indexPath.row] to
Cell.timeLabel?.text = NSString(format: "%#", alarms[indexPath.row])
And you should change the let keyword used for declaring the Cell object inside func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell method as follows:
var cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("alarmCell") as UITableViewCell alarmCell
Define Cell as
var cell: AlarmCell = self.tableView.dequeueReusableCellWithIdentifier("alarmCell") as UITableViewCell AlarmCell
then setTextLabel(or your Custom Label ) text as
cell.textLabel?.text = alarms[indexPath.row] as NSString
comment below if any query as this working perfectly for me

Subclassing UITableViewCell on Swift - Error edit UITableView rows

Using Swift: I've hooked up an NSFetchedResultsController to a UITableView, and created a subclass of UITableViewCells for the TableView cells.
The UITableViewCells format and display correctly. But the app crashes as soon as I try editing the rows (adding or deleting).
For testing, I've used the most basic subclass possible: An empty subclass, and it still crashes. The original UITableViewCell works normally.
Any ideas? Swift bug? The crash goes straight to the top of the stack. I get nothing in the console.
Works:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let task = fetchedResultController.objectAtIndexPath(indexPath) as Tasks
cell.textLabel.text = task.desc
return cell
}
Doesn't work:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell2
let task = fetchedResultController.objectAtIndexPath(indexPath) as Tasks
cell.textLabel.text = task.desc
return cell
}
When subclass is:
import UIKit
class UITableViewCell2: UITableViewCell {
}
If you create table cells programmatically (not in InterfaceBuilder) you have to register the table cell class with
TableView.registerClass(..., forCellReuseIdentifier...)

Resources