I want to change the height of a UITableViewCell when it's selected because I'm using different cellPrototypes for selected and non-selected cells.
The cells are in a UITableView which is embedded in an UIView, which is the TableView's delegate and datasource.
But when I try to check whether the cell at indexPath is selected, I am apparently producing an infinite recursion.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat cellHeight = 64;
if ([tableView cellForRowAtIndexPath:indexPath].selected) {
cellHeight = 128;
return cellHeight;
}
Could you help me finding a working solution, please?
What you'r doing is wrong, because you call cellForRowAtIndexPath which cause the tableView to call heightForRowAtIndexPath, so you have the infinite recursion.
The best option to achieve what you want is by having an object to save the index path of the selected cell to make it wider
1) first have a global variable call it NSIndexPath currentSelectedCellIndex
2) in cellDidSelectCellAtIndexPath change the variable and reload the cell as follows
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
currentSelectedCellIndex = indexPath;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
3) finally in heightForRowAtIndexPath return the new height for the selected cell
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == currentSelectedCellIndex.row) {
// for example 60
return 60;
}
return 44;
}
Try to reload the tableView on didSelectRowAtIndexPath method.
Related
A UITableView which have UITableViewCell of 20 rows, each row do have one button on it, click button I want to drop the UITableViewCell to bottom to UITableView.
Is there any delegate method which do for it.
Your feedback will be appreciated.
Yes, there is a method on tableview
- (void)moveRowAtIndexPath:(NSIndexPath*)indexPath
toIndexPath:(NSIndexPath*)newIndexPath
You can read more https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/moveRowAtIndexPath:toIndexPath:
Yes, there is one!
Use below delegate methods for that:
have to return YES to allow moving of tableView row in canMoveRowAtIndexPath delegate method as per your requirement,
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) // Don't move the first row
return NO;
return YES;
}
Then, use moveRowAtIndexPath delegate method to Updating the data-model array for the relocated row accordingly,
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
//Updating the data-model array
}
Like moveRowAtIndexPath in button click,
- (void)btnClick:(UIButton*)sender
{
UITableViewCell *cell = (UITableViewCell *)sender.superview;
NSIndexPath *indexpath = [tableView indexPathForCell:cell];
//Change accordindly as per requirement
[tableView moveRowAtIndexPath:[NSIndexPath indexPathForItem:indexpath.row inSection:indexpath.section] toIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexpath.section]];
}
Also, read Apple documentation for same:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/ManageReorderRow/ManageReorderRow.html
(note: The tableView I am using is from the Parse.com iOS SDK - PFQueryTableViewController)
Scenario = I have a TableViewController that has two different types of cells (each with their own identifier). Each object upon being queried and loaded into the datasource is checked if a key on the object is true. Depending on the result I dequeueReusableCellWithIdentifier to the correct cell.
-(PFTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
myTableViewCell *cell;
if ([object[#"orientation"] isEqualToString:#"left"] || [object[#"orientation"] isEqualToString:#"right"]) {
myTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
else {
myTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell2"];
}
This all does its job. Each cell is being loaded at the correct indexPath.row and everything. Problem is my tableView "Row Height" itself does not readjust for the new cell. This causes overlapping of cells and makes everything ugly. I can tell the tableView in storyboard to set the row height to whatever the larger of the two cell heights is, but that leaves big spaces in-between cells too which also makes it look ugly.
Question = It is my belief (and correct me if I'm wrong) that I need to use the
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
method in order to achieve this. Where I need help is I am not sure how to set the height of each cell at indexPath depending upon the 'identifier' that I gave each cell in the cellForRowAtIndexPath method.
What I'm looking for = Something like this (please excuse the SuedoCode)
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([cell.identifier isEqual:#"Cell"] {
return 100;
}
else {
return 200;
}
}
ANSWER: I figured it out! (I marked the answer below as accepted because it pointed me in the right direction)
Because I am using a PFQueryTableViewController all I had to do this...
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
PFObject *object = [self.objects objectAtIndex:indexPath.row];
if ([object[#"orientation"] isEqual:#"left"] || [object[#"orientation"] isEqual:#"right"]) {
return 100;
}
else {
return 200;
}
}
First, some things to keep in mind. heightForRowAtindexPath is calledbefore CellForRowatIndexPath, and simply says, if object is at indexPath X, then return Y or Z.
The more correct approach might be to subclass the tableCell class, set a property in the .h file and then figure out the path... I'll give you a dirty way :)
Create an NSMutableArray property (don't forget to init it somewhere/somehow), and based on your dataSource, populate it with Height A or Height B (a float). Now, back in heightForRowAtIndexPath, you can say something to the effect of:
return (int)self.myMutableArray[indexPath.row];
I want to open more information in small view of every cell selection , like exapnding able view cell.i am taaching screen shot also. I am not able to dot this plase help.How it can be done!
I am using below code , it is working fine but how to show hidden view on particular cell position
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect cell
[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
// Toggle 'selected' state
BOOL isSelected = ![self cellIsSelected:indexPath];
// Store cell 'selected' state keyed on indexPath
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
[selectedIndexes setObject:selectedIndex forKey:indexPath];
// This is where magic happens...
[tbleLectures beginUpdates];
[tbleLectures endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
// If our cell is selected, return double height
if([self cellIsSelected:indexPath]) {
return kCellHeight * 2.0;
// return 44;
}
// Cell isn't selected so return single height
return kCellHeight;
}
You can reload your tableview to make it effective or reload just the cell in your tableView:didSelectRowAtIndexPath::
// This is where magic happens...
[tbleLectures beginUpdates];
[tbleLectures reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPathOfYourCell] withRowAnimation:UITableViewRowAnimationNone];
[tbleLectures endUpdates];
If you want to change the height of the cell then you should reload the table view and use
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
delegate method to show/hide detail view
another logic is to maintain the state of the tableView and populate cells based on the state of the tableView. The tableView should have isSelectedCell/notSelected state. if tableView is selected then number of returning cell always +1 (One for detail cell); and Insert the detail cell after selected cell. to maintain the index of selected cell you might need class variable.
For this kind of work in iOS you need to have some logic!
Hope this helps
I have a table view form created using Static Cells in IB/Storyboard. However, I need to hide some of the cells at runtime depending on certain conditions.
I have found a few 'answers; to this question on SO, e.g.
UITableView set to static cells. Is it possible to hide some of the cells programmatically?
.. and they focus on setting the height of the cell / row to 0. This is great, except I now get exceptions from AutoLayout because the constraints can't be satisfied. How do I get around this last problem? Can I temporarily disable Auto-Layout for a subview? Is there a better way to be doing this in iOS7?
I found the best way to do this is to simply handle the numberOfRowsInSection, cellForRowAtIndexPath and heightForRowAtIndexPath to selectively drop certain rows. Here's a 'hardcoded' example for my scenario, you could do something a little smarter to intelligently remove certain cells rather than hard code it like this, but this was easiest for my simple scenario.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (indexPath.section == 0 && hideStuff) {
cell = self.cellIWantToShow;
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height = [super tableView:tableView heightForRowAtIndexPath:indexPath];
if (indexPath.section == 0 && hideStuff) {
height = [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]];
}
return height;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger count = [super tableView:tableView numberOfRowsInSection:section];
if (section == 0 && hideStuff) {
count -= hiddenCells.count;
}
return count;
}
Hide the cells on the storyboard and set the height to 0:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let cell: UITableViewCell = super.tableView(tableView, cellForRowAtIndexPath:indexPath)
return cell.hidden ? 0 : super.tableView(tableView, heightForRowAtIndexPath:indexPath)
}
}
If you ensure there's no constraint touching the bottom edge of the cell, autolayout shouldn't barf (tested on iOS 6.0, 6.1, 7.0). You'll still 'anchor' to the top edge and have to pin the heights. (You can do the reverse and anchor to the bottom, of course.)
If your layout depends on both the top and bottom edge positions, it may be possible to programmatically remove the constraints (they're just objects, after all).
The simplest way is to change height of sections and rows. It works for me.
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section == 3) {
return 0;
} else {
return [super tableView:tableView heightForHeaderInSection:section];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (indexPath.section == 3) {
cell.hidden = YES;
return 0;
} else {
cell.hidden = NO;
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
The kosher way to do this is to use dynamic cells, setting the row height to 0 is a hack. Static cells are very convenient but limited in functionality.
I managed to avoid exceptions from Auto Layout by first removing the constraints on the cell's contentView programmatically in viewDidLoad, and then setting that cell's height to 0 in heightForRowAtIndexPath.
I have found a way that allows you even row animations and is working on iOS 8.3. All you need is to implement the tableView:numberOfRowsInSection: data source method and then add/delete row by UITableView methods
insertRowsAtIndexPaths:withRowAnimation: and deleteRowsAtIndexPaths:withRowAnimation:.
Here is example of hiding exact row based on UISwitch state:
- (IBAction)alowNotif:(id)sender {
UISwitch *sw = (UISwitch*)sender;
NSIndexPath *index = [NSIndexPath indexPathForRow:5 inSection:0];
if ([sw isOn]) {
[self.tableView insertRowsAtIndexPaths:#[index] withRowAnimation:UITableViewRowAnimationAutomatic];
}
else {
[self.tableView deleteRowsAtIndexPaths:#[index] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (![notifications isOn]) {
return 5;
}
return 6;
}
As was mentioned above by #algal, numberOfRowInSection: is still UITableViewDataSource method, so one does not simply know how long its gonna work.
The best way for me was to modify numberOfRowsInSection method. I removed datasource which i did not want to display. Best solution for me, because everything is in one function.
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section==0) {
NSUInteger productCount = _products.count;
for(loop trough your data) {
if(your condition is true)
productCount--;
}
}
return productCount;
} else
return self.groups.count;
}
Implemented this too, for a tableview in a StoryBoard. My cells are embedded in sections with a header, represented by that blue cube in xcode6 IB. Indeed if you implement heightForHeaderInSection, heightForFooterInSection and titleForHeaderInSection,titleForFooterInSection you can access the headers when the table is displayed, and return 0.0 and nil respectively, and return 0 for numberOfRowsInSection.
Basically it all works fine, except that for every cell hidden a ca. 10 pixel high vertical space remains for every cell (section) you hide. Any idea what that could be?
How do you disable selecting only a single cell in a UITableView? I have several, and I only want the last to be disabled.
To stop just some cells being selected use:
cell.userInteractionEnabled = NO;
As well as preventing selection, this also stops tableView:didSelectRowAtIndexPath: being called for the cells that have it set. It will also make voiceover treat it the same as a dimmed button (which may or may not be what you want).
Note that if you have interactive elements in the cell (ie. switches/buttons), you'll need to use cell.selectionStyle = UITableViewCellSelectionStyleNone; instead and then make sure to ignore taps on the cell in tableView:didSelectRowAtIndexPath:.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
Throw this in your custom Table VC:
// cells lacking UITableViewCellAccessoryDisclosureIndicator will not be selectable
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType != UITableViewCellAccessoryDisclosureIndicator) {
return nil;
}
return indexPath;
}
// disabled cells will still have userinteraction enabled for their subviews
- (void)setEnabled:(BOOL)enabled forTableViewCell:(UITableViewCell *)tableViewCell
{
tableViewCell.accessoryType = (enabled) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
// if you dont want the blue selection on tap, comment out the following line
tableViewCell.selectionStyle = (enabled) ? UITableViewCellSelectionStyleBlue : UITableViewCellSelectionStyleNone;
}
Then to enable/disable selection for someTableViewCell, do this:
[self setEnabled:state forTableViewCell:someTableViewCell];
You're done and can ship.
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self numberOfRowsInSection] == [indexPath row]) {
return nil;
} else {
return indexPath;
}
}
the last row of the table will not be selected
As I mentioned in another thread all the above methods are not solving the problem precisely. The correct way of disabling a cell is through the method
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
and in that method one has to use
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
which disables cell selection but still allows the user to interact with subviews of the cell such as a UISwitch.
The cleanest solution that I have found to this only makes use of the delegate method willDisplayCell.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == 0) //<-----ignores touches on first cell in the UITableView
{ //simply change this around to suit your needs
cell.userInteractionEnabled = NO;
cell.textLabel.enabled = NO;
cell.detailTextLabel.enabled = NO;
}
}
You don't have to take any further action in the delegate method didSelectRowAtIndexPath to ensure that the selection of this cell is ignored. All touches on this cell will be ignored and the text in the cell will be grayed out as well.
with iOS 6.
You can use the following delegate method and return NO in case you don't it to be selected and YES in case you want it to be selected.
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
return indexPath.section == 0;
}
Try this in swift:
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
If anyone wondering how to achieve this in swift then here is my code. I am using Xcode 7 and tested using iPad Retina(iOS 9).
cell.selectionStyle = UITableViewCellSelectionStyle .None
cell.userInteractionEnabled = false
Try to place this two line code whether you want. In my case I have used this in this method for displaying cells.
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
Remember this two line code will block any kind of selection or interaction to your cells but you can only use the first line individually if you want. That is...
cell.selectionStyle = UITableViewCellSelectionStyle .None
Only this line will block the selection to your cells.
However the second line will make the cell "Read-Only". That is..
cell.userInteractionEnabled = false
Thanks
Hope this helped.