how to increase and resize the tableview cell in iphone? - iphone

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 50;
}
In this place, i would like to add a button dynamically to increase the height of the cell, so when user clicks upon that button it should be increase the height of cell and then click again to resize the height.
I want to something like :
-(void)IncreaseCell
{
UIButton *DownArrow = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[DownArrow addTarget:self
action:#selector(IncreaseCell:)
forControlEvents:UIControlEventTouchDown];
//[DownArrow setTitle:#"Arrow" forState:UIControlStateNormal];
DownArrow.frame = CGRectMake(121.0, 112.0, 72.0, 37.0);
[cell.contentView addSubview:DownArrow];
UIImage *buttonDown = [UIImage imageNamed:#"friendsDownArrowButton.png"];
[DownArrow setBackgroundImage:buttonDown forState:UIControlStateNormal];
[cell.contentView addSubview:DownArrow];
}

You will need to create a NSMutableArray as an instance variable, in which you keep track of all the cells, that you want to have "increased".
#interface YourTableViewController : UITableViewController {
NSMutableDictionary *increasedRows;
}
Remember to alloc/init that variable.
To get your cell increased:
-(void)increseCell: (BOOL)status forIndexPath: (NSIndexPath*)indexPath {
[increasedRows setObject:[NSNumber numberWithBool:status] forKey: indexPath];
[self.tableView beginUpdates]; //Delete if you don't want it animated
[self.tableView endUpdates]; //Delete if you don't want it animated
// [self.tableView reloadData]; //Uncomment if you don't want it animated
}
You change your tableView: heightForRowAtIndexPath: declaration to check this dictionary for your indexPath.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// Check if cell is increased, return custom height
if([[increasedRows objectForKey:indexPath] boolValue]) {
return 150;
}
// Cell isn't increased so return regular height
return 50;
}
This method will allow you to do it for every row individually and allows you to animate it.

You need to change your implementation of heightForRowAtIndexPath.
Add some storage, like an array, which holds flags indicating if a row is expanded or not. When a button is tapped, get the index path of the cell that it is on and edit the array contents to set / unset the flag. Reload the table view (or the row for the changed index path).

All you have to do is declare a variable:
#interface yourViewController ()
{
CGFloat cellSize;
}
And in your heightForRowAtIndexPath::
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return cellSize;
}
in your viewDidLoad
-(void)viewDidLoad
{
[super viewDidLoad];
cellSize = 50;
}
and last in your increaseCell:
-(void)IncreseCell {
cellSize += 10;
[self reloadData];
}

what i did in one of my project is i created a custom cell and add subview to it statically
1 st scene non collapsed view
custom cell shows only button on click of which it will show extended view by increasing size of cell..
i.e on click of button you should increse size of cell using function..
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
BOOL check=[[booleanArrayofrows objectAtIndex:indexPath.row] boolValue];
if (check)
{
return 180; ..expanded view height
}
return 40; collapse view height
}
}

Related

How dynamically change UITableViewCell size for the last cell

I have a UITableView with 16-20 cells inside with dynamic cell size. When a cell expands it self it should also move itself to the top of the screen. I did that using "UITableView setContentOffset" method. It works well except for the last cell in table, it's not able to move itself to the top.
I tried altering the frame & content size of UITableView but none of those were working for me!
Any Idea?
[UPDATE]
Here is a part of code: (it's inside the UITableViewCell, so self is pointing to current cell)
HomeViewController *tempViewController = (HomeViewController *) delegate;
UIView *commentField;
/*Skipping lines of codes manipulating commentField */
//Adding a subview to current cell which needs more space
[self addSubview:commentField];
//Expanding cellSize to EXPANDED_CELL_HEIGHT
//ViewController has access to cell size property and using that to determine each cell size.
[self setCellSize:(EXPANDED_CELL_HEIGHT)];
//Reloading UITableView to reflect the cell size change with animation
[[tempViewController tableView] beginUpdates];
[[tempViewController tableView] endUpdates];
[[tempViewController tableView] setContentOffset:CGPointMake(0, self.frame.origin.y) animated:YES];
and in my view controller (as I said earlier) I'm getting cellSize form cell itself
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [(BaseTableViewCell *)[cellContainer objectAtIndex:indexPath.row] cellSize];
}
You can change the height of the cell using the tableview delegate method heightForRowAtIndexPath: When indexPath.row equals your last row, return the height you would like.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == i)//i being whatever row index you want to change
{
return 60.0;//or float size you want
}
else
{
return 30.0;
}
}
Or you can just modify the bounds of the cell inside willDisplayCell:. If your using a custom tableview cell, just shrink the subviews to whatever frame you'd like and make the cell background clear etc.

Enabling & Disabling UI Toolbar in Expanded Cell

I have a cell (videoCell) that when unselected is 100px in height, and when selected expands to 144px. In the expanded 44 pixels I have placed a toolbar (detailToolbar) through storyboard.
Now here is my problem. If user interaction is enabled for the toolbar, the bar buttons work, and to simply close the cell I can just tap any of the original 100px of the cell. However, the problem arises when I'm attempting to expand a cell. If I tap the bottom 44px of the unselected cell, nothing is triggered, but will only respond if the top 56 pixels are selected. I'm assuming that the detailToolbar behind the cell is preventing it from working.
Here's some my code. I'm using Simon Lee's tutorial to expand the cells.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// If our cell is selected, return double height
if([self cellIsSelected:indexPath]) {
return 144.0;
}
// Cell isn't selected so return single height
return 100.0;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Deselect
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
self.tableView.clipsToBounds = YES;
// Store cell 'selected' state keyed on indexPath
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
[selectedIndexes setObject:selectedIndex forKey:indexPath];
// This is where magic happens...
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
I've answered it myself. I did the following in the Did Select Row method:
for(NewsCell *cell in [self.tableView visibleCells]) {
BOOL cellIsSelected = isSelected;
[cell.detailToolbar setUserInteractionEnabled:cellIsSelected];
}
It worked flawlessly!

When click on button (button in cell) that particular cell should be changed?

There are 1 table on uiview and I want to change the cell height when button pressed other cell's height remain same
Pass the button press event to the view controller through delegate methods and reload the table view as follows.
[self.tableView reloadData];
In view controller (ie., datasource for the table view), implement heightForRowAtIndexPath method and return the height as required.
You can change the cell height in the delegate method
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row == clickedRow)
return newHeitht;
return cellHeight;
}
you can set some condition in button click and reload the tableview using [tableView reloadData]. this function will be called. return a new height for the particular cell.
-(void)buttonClick {
[self.tableview reloadData];
selectedRow = //do something here
}
and in your UITableview Datasource
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
if(indexPath.row == selectedRow)
return selectedRowHeight;
else
return defaultHeight;
}
1) Create a custom UITableViewCell class.
2) Populate you cell however you see fit. I use a 'hydrateWithArray' type function myself.
3) Once the data has been populated, resize and reposition elements using 'sizeToFit' functions to force labels to conform to the size of whatever you put into them. protip: by setting the frame of the label first, and setting the numer of lines to 0... when you fill the text and sizetofit, it will stretch the label vertically only and force the width to stay the same.
4) Create a seperate function (mine is called calculatedHeight) that returns a float and returns the height that you would like the cell to be in the table (based on the repositioned objects from step 3).
- (float)calculatedHeight {
return textLabel.frame.origin.ytextLabel.frame.size.height5;
}
5) In your UITableView class, you'll need to import your tableViewCell class and create a dummy cell object. You're going to use this class to calculate how tall each cell needs to be. Then in the heightOfRowAtIndex method....
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
float height;
if ( !dummyCell ) dummyCell = [[CustomCell alloc] initWithFrame:CGRectMake(0,0,0,0) reuseIdentifier:#"myCell"];
[dummyCell hydrateWithTweet:[tableArray objectAtIndex:indexPath.row]];
height = [dummyCell calculatedHeight];
if ( height == 0 ) height = 50;
return height;
}
This is a pretty simple example so you may need to go crazy with the error checking in your particular use, but this should at least point you in the right direction. Enjoy!
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (isSearching && indexPath.row == selectedIndex) {
return 110;
}
else {
return rowHeight;
}
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:toReloadRows withRowAnimation: UITableViewRowAnimationNone];
[self.tableView endUpdates];
then
[self.tableView beginUpdates];
[self.tableView endUpdates];

UITableViewCell expand on click

Lets say we have a custom UITableViewCell
So whenever I click custom button on cell.. it should expand to the some extent (you can say 40 height more...) and when i click again to the same custom button it should collapse to the previous height.
Developer's please guide me.. how can I achieve this task
I'm not going to say anything here to contradict the accepted answer considering it is perfectly correct. However, I am going to go into more detail on how to accomplish this. If you don't want to read through all this and are more interested in playing with the source code in a working project, I've uploaded an example project to GitHub.
The basic idea is is to have a condition inside of the method -tableView: heightForRowAtIndexPath: that determines whether or not the current cell should be expanded. This will be triggered by calling begin/end updates on the table from within -tableView: didSelectRowAtIndexPath: In this example, I'll show how to make a table view that allows for one cell to be expanded at a time.
The first thing that you'll need to do is declare a reference to an NSIndexPath object. You can do this however you want, but I recommend using a property declaration like this:
#property (strong, nonatomic) NSIndexPath *expandedIndexPath;
NOTE: You do not need to create this index path inside viewDidLoad, or any other similar method. The fact that the index is initially nil will only mean that the table will not initially have an expanded row. If you would rather the table start off with a row of your choice expanded, you could add something like this to your viewDidLoad method:
NSInteger row = 1;
NSInteger section = 2;
self.expandedIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
The next step is to head on over to your UITableViewDelegate method -tableView: didSelectRowAtIndexPath: to add the logic to alter the expanded cell index based on the users selection. The idea here is to check the index path that has just been selected against the index path stored inside the expandedIndexPath variable. If the two are a match, then we know that the user is trying to deselect the expanded cell in which case, we set the variable to nil. Otherwise, we set the expandedIndexPath variable to the index that was just selected. This is all done between calls to beginUpdates/endUpdates, to allow the table view to automatically handle the transition animation.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates]; // tell the table you're about to start making changes
// If the index path of the currently expanded cell is the same as the index that
// has just been tapped set the expanded index to nil so that there aren't any
// expanded cells, otherwise, set the expanded index to the index that has just
// been selected.
if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
self.expandedIndexPath = nil;
} else {
self.expandedIndexPath = indexPath;
}
[tableView endUpdates]; // tell the table you're done making your changes
}
Then the final step is in another UITableViewDelegate method -tableView: heightForRowAtIndexPath:. This method will be called after you've triggered beginUpdates once for each index path that the table determines needs updating. This is where you'll compare the expandedIndexPath against the index path that is currently being reevaluated.
If the two index paths are the same, then this is the cell that you wish to be expanded, otherwise it's height should be normal. I used the values 100 and 44, but you can use what ever suits your needs.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Compares the index path for the current cell to the index path stored in the expanded
// index path variable. If the two match, return a height of 100 points, otherwise return
// a height of 44 points.
if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
return 100.0; // Expanded height
}
return 44.0; // Normal height
}
Implement heightForRowAtIndexPath to calculate the right height. Then in the code for your button, force the table to reevaluate each cell's height with beginUpdates plus endUpdates:
[self.tableView beginUpdates];
[self.tableView endUpdates];
Changes to the tableview cells' heights will automatically be calculated with heightForRowAtIndexPath and the changes will be animated too.
In fact, instead of a button on your cell that does this, you might even just make selecting the cell do this in didSelectRowAtIndexPath.
Instead of using [tableView beginUpdates] and [tableView endUpdates] , I'm using the [tableView reloadRowsAtIndexPath:... withRowAnimation:...] method inside the didSelectRowAtIndexPath method.
I prefer this, because I had some problems with elements that should show, when I expand my UITableViewCell, when I used the begin & end updates methods. Another point is that you can choose between some animations like: Top, Bottom, Left, Right...
I have created an open source library for this. You just implement collapse and expand delegates in your code and voilà! you can also perform any drawings and animations. check out this.
I've made a reusable component that will do exactly what you're talking about. It's pretty easy to use, and there's a demo project.
GCRetractableSectionController on GitHub.
This is Mick's answer but for Swift 4. (IndexPath replaces NSIndexPath, which comes with an empty IndexPath as nil would crash Swift. Also, you can compare two instances of IndexPath using ==)
Declare the expandedIndexPath property.
var expandedIndexPath = IndexPath()
Optional viewDidLoad part.
expandedIndexPath = IndexPath(row: 1, section: 2)
Then the didSelectRow part.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.beginUpdates()
if indexPath == expandedIndexPath {
expandedIndexPath = IndexPath()
} else {
expandedIndexPath = indexPath
}
tableView.endUpdates()
}
Then the heightForRow part.
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath == expandedIndexPath {
return 100
}
return 44
}
I used Gcamp's source code and made my own version.
1) In a loadView method initialize a mutable array where you will save expanded or non-expanded states of your sections. It is critical to save expanded statuses in a separate array, that is not destroyed while table view scrolls (for instance if you store it in a headerView it will be redrawn and forget weather it was expanded or not). In my case it is _sectionStatuses array.
- (void)loadView
{
// At the beginning all sections are expanded
_sectionStates = [NSMutableArray arrayWithCapacity:self.tableView.numberOfSections];
for (int i = 0; i < self.tableView.numberOfSections; i++) {
_sectionStates[i] = [NSNumber numberWithBool:YES];
}
}
2) Create a custom headerView for a section with a button for expanding. Delegate an action from a button in your headerView to your TableViewController using delegation pattern. You can find suitable images in Gcamp's source code.
3) Create an action to remove or add rows. Here _foldersArray is my structure, that contains all the data. My section's headerView - MCExpandableAccountHeaderView knows it's own section number - I transfer it there when I create header views for each section. It is critical to transfer it to this method, since you have to know which section is now expanded or stretched.
- (void)expandClicked:(MCAccountHeaderView *)sender
{
MCExpandableAccountHeaderView *expandableAccountHeaderView = (MCExpandableAccountHeaderView*)sender;
// Finding a section, where a button was tapped
NSInteger section = expandableAccountHeaderView.section;
// Number of rows, that must be in a section when it is expanded
NSUInteger contentCount = [_foldersArray[section - 1][#"folders"] count];
// Change a saved status of a section
BOOL expanded = [_sectionStates[section] boolValue];
expanded = ! expanded;
expandableAccountHeaderView.expanded = expanded;
_sectionStates[section] = [NSNumber numberWithBool:expanded];
// Animation in a table
[self.tableView beginUpdates];
NSMutableArray* modifiedIndexPaths = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < contentCount; i++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:i inSection:section];
[modifiedIndexPaths addObject:indexPath];
}
if (expandableAccountHeaderView.expanded) [self.tableView insertRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade];
else [self.tableView deleteRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
// Scroll to the top of current expanded section
if (expandableAccountHeaderView.expanded) [self.tableView scrollToRowAtIndexPath:INDEX_PATH(0, section) atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
4) It is also important to return correct number or rows in a section depending on wheather it is expanded or not.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
BOOL expanded = [_sectionStates[section] boolValue];
return expanded ? [_foldersArray[section - 1][#"folders"] count] : 0;
}
initialize iSelectedIndex = -1; and declare
UITableView *urTableView;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 10; //Section count
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3; //row count
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell.textLabel setText:[NSString stringWithFormat:#"sec:%d,row:%d",indexPath.section,indexPath.row]];
return cell;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
// adding a label with the tap gesture to the header in each section
headerLabel = [[UILabel alloc]init];
headerLabel.tag = section;
headerLabel.userInteractionEnabled = YES;
headerLabel.backgroundColor = [UIColor greenColor];
headerLabel.text = [NSString stringWithFormat:#"Header No.%d",section];
headerLabel.frame = CGRectMake(0, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height);
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(gestureTapped:)];
[headerLabel addGestureRecognizer:tapGesture];
return headerLabel;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
return 50.0; //adjust the height as you need
}
- (void)gestureTapped:(UITapGestureRecognizer *)sender{
UIView *theSuperview = self.view; // whatever view contains
CGPoint touchPointInSuperview = [sender locationInView:theSuperview];
UIView *touchedView = [theSuperview hitTest:touchPointInSuperview withEvent:nil];
if([touchedView isKindOfClass:[UILabel class]])
{
if (iSelectedIndex != touchedView.tag) { //if new header is selected , need to expand
iSelectedIndex = touchedView.tag;
}else{ // if the header is already expanded , need to collapse
iSelectedIndex = -1;
}
[urTableView beginUpdates];
[urTableView endUpdates];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// Show or hide cell
float height = 0.0;
if (indexPath.section == iSelectedIndex) {
height = 44.0; // Show the cell - adjust the height as you need
}
return height;
}
For me it works to use:
on UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Did select row: \(indexPath.row).")
tableView.beginUpdates()
tableView.endUpdates()
}
on selectable/expandable UITableViewCell
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
configStyle(selected)
}
Important! tableView.rowHeight is .automatic and UITableViewCell is constraint to enable automatic height calculation, i.e. its height constraint are clearly defined like constraint to top/bottom or height constraints added or label intrinsic content size is used.
To add to 0x7fffffff's answer, I found I needed an extra condition in the if statement within didSelectRowAtIndexPath - thus:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates];
if (self.expandedIndexPath && [indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
self.expandedIndexPath = nil;
} else {
self.expandedIndexPath = indexPath;
}
[tableView endUpdates];
}
Following this medium article on how to expand the cells based on the tap of a button and setting the numbersOfLine for a specific label, I was able to perform the animation using
tableView.beginUpdates()
tableView.performBatchUpdates({
cell.description.numberOfLines = !expanded ? 0 : 3
}, completion: nil)
tableView.endUpdates()
Notice performBatchUpdates is only available in iOS 11⬆️

Button placement on table footer view

I have a table view wherein the number cells are not fixed and will keep on changing. I have to show two action buttons after my last cell in the table footer. How can I place them properly after my last cell? I know the pixel spacing between last cell and these buttons. Any code sample will be really helpful.
Have you tried sticking them in a view and setting that as the footer view?
You need to give it the correct height before you add it; the width is automatically set to the width of the table. Either set it to a sensible width (e.g. 320) and use autoresizing or use a custom UIView subclass and implement -layoutSubviews.
You could always add two buttons to the final cell.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
return [myCellDataArray count]+1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.row > [myCellDataArray count]) {
cell = [tableView dequeueReusableCellWithIdentifier:#"Button Cell"];
if (cell == nil) {
UIButton *firstButton = [[UIButton alloc] init];
//customization
UIButton *secondButton = [[UIButton alloc] init];
//customization
[cell addSubView:firstButton];
[cell addSubView:firstButton];
}
} else {
// normal stuff
}
If you want to customize existing buttons you need to set it's tag to something unique, i.e. firstButton.tag = 100 and then set firstButton by firstButton = (UIButton *)[cell viewWithTag:100];. Make sure you define firstButton so that it's in scope!
Hope this helps!