I want to resize a UITableViewCell when it is selected. In iOS 7 I designed two table view cells of different heights and replaced the one with the other when selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// ...
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
// ...
}
I set the height correspondingly:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([self.tableView.indexPathForSelectedRow isEqual:indexPath]) {
return 163.0f;
}
else {
return 60.0f;
}
}
Problem:
When selecting the row on iOS 8 it will load the new cell but it won't change the height of the cell. Instead it will break one of my constraints, enforcing the systems "<NSLayoutConstraint:0x7f9fdb72b1f0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f9fdb7009f0(60.6667)]>" constraint.
My first solution was to use - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath instead of - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath but this doesn't work on iOS 7.
Implementing both methods will not produced the desired result on either OS.
Related
I am making a chat page, where I scroll on top, I reload the previous message from API. After getting response, reload the table, and the table moves to first cell. But I want it to be stay where it was during loading the messages. Following are the solutions that I tried:
Solution 1:
Declared this:
NSMutableDictionary *cellHeightsDictionary;
Added this in viewDidLoad:
self.chatTableView.rowHeight = UITableViewAutomaticDimension;
cellHeightsDictionary = #{}.mutableCopy;
Then added the below mentioned methods:
// save height
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[cellHeightsDictionary setObject:#(cell.frame.size.height) forKey:indexPath];
}
// give exact height value
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
if (height) return height.doubleValue;
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
And here reloading my tableview:
- (void)reloadAllMessagesHere {
[UIView performWithoutAnimation:^{
[self.chatTableView reloadData];
[self.chatTableView beginUpdates];
[self.chatTableView endUpdates];
}];
}
But still the same issue is happening.
Then I thought I should use the bottom contentOffset of the page. So, I used this method to reload the data:
Solution 2:
Removed all above codes and used these methods:
For TableView Height:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
and tableview reload
- (void)reloadWithoutMoving {
CGPoint contentOffsetVal = CGPointMake(0, self.chatTableView.contentSize.height - self.chatTableView.bounds.size.height);
[self.chatTableView reloadData];
[self.chatTableView layoutIfNeeded];
[self.chatTableView setContentOffset:contentOffsetVal];
}
BUT NO LUCK!!
Here check the two images shown below:
Image 1: Here I am loading the tableview.
Image 2: Here I got the response and the data shown in Image 1 move to bottom automatically
How can I fix this?
[P.S. Please don't mark this as duplicate as I have tried previous the solutions but couldn't fix it.]
I have a custom uitableviewcell, which behaves different when run on iOS 8. It works completely fine on iOS 7. The cell get partially highlighted on selection. Here is a screenshot below for running on iOS 8:
The code that handles iOS 7 & 8 cases are as below:
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([tableView respondsToSelector:#selector(setLayoutMargins:)]) {
return UITableViewAutomaticDimension;
} else {
return [self heightForCellAtIndexPath:indexPath];
}
}
Autolayout of my UITableViewCell works as expected when viewing, but when I selecting a row, the UI will break, and the height of this cell will also back to default value.
For cell-related code, I just followed this tutorial "Using Auto Layout in UITableView for dynamic cell layouts & variable row heights"
Unfortunately, this tutorial doesn't cover selection part of UITableViewCell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell = ...
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.navigationController pushViewController:MyViewController animated:YES];
}
I have a custom UITableViewCell, and I it expands on selection. What I want to do is make the cells height go back to normal (44), if the cell that was selected was reselected. Here is my code:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath isEqual:self.selectedCell] && ![[tableView indexPathForSelectedRow] isEqual:indexPath]) {
return 100;
}
return 44;
}
The code works fine, but it seems to be ignoring the 2nd term in the if statement. Apparently I'm doing it wrong. Is there a way to fix it?
I don't know why you compare cell with indexPath.
[indexPath isEqual:self.selectedCell]
If you just want expand selected cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[tableView indexPathForSelectedRow] isEqual:indexPath])
{
return 100;
}
return 44;
}
UITableViewDelegate has deselect method
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
You need reload cell in this delegate
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
You need to store that particular index path in array to indicate that it is expanded Lets say you had selected 5 row that store that row number in an array, and reload that row after selecting it
Here is small code snippet that can help you to achieve this
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if([_arrExpandedRows containsObject:indexPath.row]){
[_arrExpandedRows removeObject:indexPath.row];
}else{
// [_arrExpandedRows removeAllObjects]; // IF you only want to make it work for single row
[_arrExpandedRows addObject:indexPath.row];
}
[tableView reloadData];
}
And in your heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([_arrExpandedRows containsObject:indexPath.row]){
{
return 100;
}
return 44;
}
So what will it do if your array contains object of expanded row it will make reduce its row height and back to 44 and if not it will make it back to 100.
And above code will also work for multiple rows to expand and collapse at same time.
I think what you want is unselect cell when tap on a selected cell.
Store selectedIndexPath and compare in delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([selectedIndexPath isEqual:indexPath])
{
[tableView deselectRowAtIndexPath:indexPath animated:1];
}
else
{
selectedIndexPath = indexPath;
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
You can do something like this:
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView* tableView;
#property (nonatomic, strong) NSMutableArray* selectedIndexPaths;
#end
<...>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:CELL_ID];
cell.textLabel.text = [NSString stringWithFormat:#"%i", (int)indexPath.row];
return cell;
}
#pragma mark UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 44.0;
if ([_selectedIndexPaths containsObject:indexPath])
{
height = 100;
}
return height;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{//store or remove selected indexPaths
if ([_selectedIndexPaths containsObject:indexPath])
{
[_selectedIndexPaths removeObject:indexPath];
}
else
{
[_selectedIndexPaths addObject:indexPath];
}
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
I have UITableView with custom cell, Swipe-to-delete works just fine in iPad simulator but it never worked on iPad device.
Here's my code:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleDelete;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates];
if (editingStyle == UITableViewCellEditingStyleDelete)
{
if(_itemsArray == nil)
NSLog(#"\n\nNIL ARRAY\n\n");
NSLog(#"\nindexPath.row = %d\nItemsArray Count:%d",indexPath.row,_itemsArray.count);
int row = [[[_itemsArray objectAtIndex:indexPath.row]valueForKey:#"itemRow"] integerValue];
[_itemsArray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[[self delegate]didDeletedBillItemRow:row];
}
[tableView endUpdates];
}
I also implemented layoutSubviews in my custom cell controller:
-(void)layoutSubviews
{
[super layoutSubviews];
}
What do you think the problem is?
May or may not work for you. I spend a while figuring out why it was not working for me. I realized it was because the cell user interaction enabled was off. I don't know why it was still working on the simulator but that fixed it for me.
I only changed the property of selection mode during editing to "Single Selection During Editing" instead of "Multiple Selection During Editing" and I'm now able to delete the rows using swipe-to-delete perfectly.