In my code i have a table with static cell inside storyboards. I'm trying to fire a method upon clicking the last static cell.
What should i write in the code to make this happen. How can i refer static cells inside the code without firing error.
In the viewController add:
#property (nonatomic, weak) IBOutlet UITableViewCell *theStaticCell;
Connect that outlet to the cell in the storyboard.
Now in tableView:didSelectRowAtIndexPath method:
UITableViewCell *theCellClicked = [self.tableView cellForRowAtIndexPath:indexPath];
if (theCellClicked == theStaticCell) {
//Do stuff
}
With static cells, you can still implement - tableView:didSelectRowAtIndexPath: and check the indexPath. One approach, is that you define the particular indexPath with #define, and check to see whether the seleted row is at that indexPath, and if yes, call [self myMethod].
Here is my take when mixing static and dynamic cells,
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let staticIndexPath = tableView.indexPathForCell(self.staticCell) where staticIndexPath == indexPath {
// ADD CODE HERE
}
}
this avoids creating a new cell.
We all are used to create the cell and configure it in cellForRowAtIndexPath
Following CiNN answer, this is the Swift 3 version that solves the issue.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let staticIndexPath = tableView.indexPath(for: OUTLET_TO_YOUR_CELL), staticIndexPath == indexPath {
// ADD CODE HERE
}
}
this approach allow to not necessary implement cellForRow method, specially if you are using static cells on storyboard.
I think you were having the same problem I was. I kept getting an error when overriding tableView:didSelectRowAt, and the reason was that I'm used to just calling super.tableView:didSelectRowAt, but in this case we don't want to do that. So just remove the call to the super class method to avoid the run-time error.
Related
I'm making an app that has a collection table view that when a cell is selected it segues into another view controller with a tableview.
I want to use this tableview to update a UIImageView in my VC. So let say i press "Situps" on the TBC i want the imageview to update to an animation that shows situps. Hope someone can understand what im trying to do, thanks for the help!
The delegate method tableView(_:didSelectRowAt:) is called when a cell is tapped on.
You can insert your logic to either segue to another viewController or update an imageView in the same viewController in this method.
for Swift 3.0:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//your code...
}
Documentation: https://developer.apple.com/reference/uikit/uitableviewdelegate/1614877-tableview
Your problem is quite simple.
use method.
tableView(_:didSelectRowAt:)
then put your method here inside did select and you can navigate to viewcontroller you want to.
You can try this:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as UITableViewCell
//so you can do whatever you want on your cell
}
Yes, you can trigger any action of a cell using the didSelect delegate method of Tableview but...
I think that what you are trying to do is to make an update in one VC from other? is that the case, what you need is to use delegation or NSNotificationCenter to send updates between classes.
I'm building a table that has two cells of the same class but of different identifiers.
I am using a segmented control to display either or.
I believe everything is hooked up properly on Storyboard, however,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let friendRequest = self.friendRequestsToDisplay[indexPath.row]
requestDirection = isAnIncomingRequest ? "IncomingRequestCell" : "OutgoingRequestCell"
if let cell = tableView.dequeueReusableCellWithIdentifier(requestDirection) as? RequestCell {
cell.configureCell(friendRequest, isAnIncomingRequest: isAnIncomingRequest)
return cell
} else {
return UITableViewCell()
}
}
FAILS AT dequeueReusableCellWithIdentifier, according to breakpoints, with:
fatal error: unexpectedly found nil while unwrapping an Optional value
After hard coding the Identifiers ("IncomingRequestCell" and "OutgoingRequestCell" in the dequeueReusable... method as suggested in the comments, it appears that these values are the source of the problem. However, they properly identify their respective UITableViewCells in IB.
Any ideas?
Why you use global variable for reuseIdentifier and update it every time? You can use local var for it.
Also avoid using dequeueReusableCellWithIdentifier, please use dequeueReusableCellWithIdentifier:forIndexPath: instead. I had some strange issues in one of my previous project because of it.
Here is quick example, which works like a charm:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let identifier = (indexPath.row == 0) ? "Cell1" : "Cell2"
let cell = tableView.dequeueReusableCellWithIdentifier(identifier, forIndexPath: indexPath)
self.configureCell(cell, atIndexPath: indexPath)
return cell
}
UPDATE: After investigation, we figured, that problem was in custom cell class initialization code.
If everything is hooked up properly - which means that you correctly set your reuseIdentifiersin IB and the UITableViewCells are prototyp cells within your UITableView (and not loaded from a nib - which requires manually registering the UITableViewCells), than just use
dequeueReusableCellWithIdentifier:forIndexPath:
I have a UITableViewCell with a UICollectionViewCell inside. I want the user to be able to scroll the UICollectionViewCell but when he taps anywhere inside the UITableViewCell I need it to be selected. Currently when the user taps the UITableViewCell outside the UICollectionViewCell it is selected properly, but when he taps inside the UICollectionViewCell nothing happens. My idea is to implement the collectionView: didSelectItemAtIndexPath: method inside the UITableViewCell and programmatically trigger a "self selection", but I can't seem to find a way to do this. If I store a reference to the table and the indexPath of the cell inside itself I will probably be able to do it, but I have a feeling that this would be a bad way of doing it. How to do this properly?
My guess is that the UITableViewCell's didSelectRowAtIndexPath: is blocking the UICollectionViewCell's collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:
I would first check if that's the case by putting breakpoints for the UITableViewCell's didSelectRowAtIndexPath: while selecting the UICollectionViewCell.
If that's the case, this answer might help you (comes with a nice tutorial too): https://stackoverflow.com/a/17120673/5465258
I think they have a similar problem to what you had.
Well, I found a solution for this, though I don't know if it's the best one.
First, I added a UITapGestureRecognizer to the UICollectionView inside my UITableViewCell, to override the component's one.
-(void)awakeFromNib
{
...
UITapGestureRecognizer *v_CollectionViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onClickCollectionView)];
[self.m_CollectionView addGestureRecognizer:v_CollectionViewTap];
...
}
Second I created a block property to hold the code for the programatic selection of a row and created a method that calls this block, setting it as the action for the previously created UIGestureRecognizer.
typedef void (^OnClickTableViewCellBlock)();
#property (nonatomic, copy) OnClickTableViewCellBlock m_OnClickBlock;
Last, when I'm creating the UITableViewCell, in the tableView:cellForRowAtIndexPath: method, I pass a block that calls the tableview:didSelectRowAtIndexPath: one.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
v_Cell.m_OnClickBlock = ^void()
{
[tableView.delegate tableView:tableView didSelectRowAtIndexPath:indexPath];
};
...
}
My solution was essentially the same as accepted answer, except I'm using Swift and decided to use delegation.
Inside your UITableViewCell subclass - CustomCell in this case - create your delegate as below (making sure to add a property for IndexPath), and wire up the UITapGestureRecognizer similar to the accepted answer:
protocol CustomCellDelegate {
func collectionViewTapped(forIndexPath indexPath: IndexPath)
}
class CustomCell: UITableViewCell {
...
var delegate: CustomCellDelegate?
var indexPath: IndexPath?
#objc func onTapCollection() {
if let indexPath = indexPath {
delegate?.collectionViewTapped(forIndexPath: indexPath)
}
}
override func awakeFromNib() {
super.awakeFromNib()
let collectionViewTap = UITapGestureRecognizer(target: self, action: #selector(onTapCollection))
collectionViewTap.numberOfTapsRequired = 1
self.collectionView?.addGestureRecognizer(collectionViewTap)
}
...
}
And then just have your UITableViewController implement your CustomCellDelegate, and assign the delegate and IndexPath for your cell in tableView(_:cellForRowAt:):
class MyViewController: UITableViewController {
...
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
// cell here is my CustomCell that I set up
cell.indexPath = indexPath
cell.delegate = self
return cell
}
...
}
extension MyViewController: CustomCellDelegate {
func collectionViewTapped(forIndexPath indexPath: IndexPath) {
self.tableView(self.tableView, didSelectRowAt: indexPath)
}
}
I'm trying to call cellForRowAtIndexPath from within heightForRowAtIndexPath in order to assign a height based on the cell's type (I'm subclassing UITableViewCell). Trivial, right? Well, calling it there causes a loop. I can't quite seem to figure out why that would be. Placing breakpoints in both methods doesn't yield anything—the delegate method cellForRowAtIndexPath never actually gets called. Take a look:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
return SubclassCellTypeOne()
default:
return SubclassCellTypeTwo()
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// Calling cellForRowAtIndexPath here causes a loop
let cell = tableView.cellForRowAtIndexPath(indexPath)!
if cell is SubclassCellTypeOne {
return UITableViewAutomaticDimension
} else {
return 100
}
}
Any idea why that's happening? And any suggestions on how to get around it? Thanks!
When a reference to a cell is made via a UITableView, (usually by iOS, when loading your view), iOS calls the methods in its lifecycle - e.g., heightForRowAtIndexPath, editingStyleForRowAtIndexPath to work out how to display it etc.
So your source of an infinite loop is that you make a reference to a cell, inside a method that is called when a reference to a cell is made ;)
To fix this, you should reference back to your data source, instead of asking the cell directly about itself. If you have a class set up as a data collection, this is easy.
Yep, you shouldn't call cellForRow inside heightForRow.
In heightForRow you have the indexPath variable. You can use indexPath.row to determine the class of the cell inside heightForRow, just like you do in cellForRow.
You could also have forgotten to set the delegate and datasource properties of the tableview. Or you are returning 0 from numberOfRowsInTable...
That could also be why you are not hitting the breakpoint inside cellForRow.
I have an iOS app with a lot of static cells (for a preferences view), so it makes sense to put all of that in storyboard, but I would like to be able to add a checkmark to them based on if the preference is set or not.
I have my delegate method setup
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
I just can't figure out how to "grab" the cell from the interface builder using the indexpath so that I can decide programmatically whether or not I should add a checkmark. I have a feeling there is some sort of superclass/delegate method I can call, but I'm not sure what it is. Thanks.
When you use static cells, you need to put them in a UITableViewController because there's magic going in there. Under the hood, it implements those data source methods for you. But you can override them. The important thing is that you need to call the super version to let it do it's job. If the method returns a value you need to return that too.
So in your case:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)
let isChecked = true // put your logic to determine whether the cell should be checked here
cell.accessoryType = isChecked ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone
// ...
return cell
}