UITableView cells do not respond to input after resizing - ios

I am adding new rows in my table view and resizing it according to its contents, but after the resize, the cell contents, like a button and didSelectRowAtIndexPath: are not being invoked.
Here is the resizing code:
CGRect frame = cell.replayTableView.frame;
int height = (model.replyComments.count*61)+3;
frame.size = CGSizeMake(cell.replayTableView.frame.size.width, height);
cell.tableviewReplay.refTable.frame=frame;

Assign a height on heightForRowAtIndexPath
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.Row == 1)
return 50.0;
else if (indexPath.Row == 2)
return 60;
}

First if you are using auto layouts you have to update constraints also .
In your code you are getting frame from a different view
CGRect frame = cell.replayTableView.frame;
after changing height
CGRect frame = cell.replayTableView.frame;
int height = (model.replyComments.count*61)+3;
frame.size = CGSizeMake(cell.replayTableView.frame.size.width, height);
but then setting it to different view
cell.tableviewReplay.refTable.frame=frame;
This may be creating issue for you.

// Declare a variable in yourController.h CGFloat tableHeight;
// set height in heightforRowAtindex
// run loop for number of rows in ur tableview data array
// put this code when ur data array is not empty i.e after responce of webservice etc
// as your tableview is and custom cells are not created programatically , so you need viewDidLayoutSubviews to reset frames
tableHeight = 0.0f;
for (int i = 0; i < [dataArray count]; i ++) {
tableHeight += [self tableView:self.multipleArticletableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
}
[self viewDidLayoutSubviews];
// put code below in viewDidLayoutSubviews method . it will work
self.multipleArticletableView.frame = CGRectMake(self.multipleArticletableView.frame.origin.x, self.multipleArticletableView.frame.origin.y,self.multipleArticletableView.frame.size.width, tableHeight);

Most common cause for this kind of issues are cells having some UI components that intercepts the user tap and do not pass on the tap to underneath cell. You can fix this by disabling user interaction on those components.
For example if you had added a UIView in your cell then call
otherView.userInteractionEnabled = NO;

Related

How to dynamically increase height of scrollview based on uitableview

Currently, I've developed project that UIScrollView, UITableView, another 3 more UIView inputs and UIButton at the last. In that page, UIScrollView height will be dynamically increased based on height of UITableView.
For UITableView there is no more scrolling. Its height will be increased as well based on how many rows are added based on JSON data loaded by Async as follow.
productHeight = 44;
productHeight *= _nsOrderItems.count;
productHeight = productHeight + 100;
if (isHeaderTap) {
self.productTableHeight.constant = 50;
} else {
self.productTableHeight.constant = productHeight;
}
//then the tableView must be redrawn
[self.productTable setNeedsDisplay];
My Problem is I want to increase height of UIScrollView based on height of UITableView.
- (void)viewDidLayoutSubviews {
[self.scrollView setContentSize:CGSizeMake(_scrollView.frame.size.width, _btnEdit.frame.origin.y + _btnEdit.frame.size.height)];
}
You can definitely do that,
First make sure your constraint of cells subView must set to top to bottom in order to calculate the height required for the cell.
Make sure your delegated are set as below
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 44;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
Set height constraint of your tableView and make outlet of that constraint.
Add below method to your class where you want to resize your tableView dynamically.
- (void)adjustHeightOfTableview
{
CGFloat height = self.tableView.contentSize.height;
//CGFloat maxHeight = self.tableView.superview.frame.size.height - self.tableView.frame.origin.y;
/*
Here you have to take care of two things, if there is only tableView on the screen then you have to see is your tableView going below screen using maxHeight and your screen height,
Or you can add your tableView inside scrollView so that your tableView can increase its height as much it requires based on the number of cell (with different height based on content) it has to display.
*/
// now set the height constraint accordingly
self.constraintHeightTableView.constant = height;
//If you want to increase tableView height with animation you can do that as below.
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
}
Call this method when you are ready with the dataSource for the table, and call the method as
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
//In my case i had to call this method after some delay, because (i think) it will allow tableView to reload completely and then calculate the height required for itself. (This might be a workaround, but it worked for me)
[self performSelector:#selector(adjustHeightOfTableview) withObject:nil afterDelay:0.3];
});
// Get the supposed to be height of tableview
CGFloat height = self.tableViewMenu.contentSize.height;
// get the height constraint of tableview and give it you respected height
self.constraintHeightTableView.constant = height;
// set the scroll
NOTE:- don't forget to disenable the scrolling of table from storyboard or programmatically
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, self.viewUserLogin.frame.size.height + height);
[self.scrollView layoutIfNeeded];

Track coordinates of UITableViewCell subview

I would like to fire an event when the subview of a UITableviewCell reaches a certain point on the screen, say for example when its origin.y reaches 44 points. It would also be nice to know if it was being scrolled up or down when it reached that point. I was playing with KVO on the frame of the subview but this seems fixed to the cell so no changes with that. Is this task possible?
Vertical position of UITableViewCell is defined by its frame property, which represents position and size of that cell within its superview, UITableView. Typically, the frame property of the cell is changing only once for every time that UITableView requests a cell from its delegate for specific index path. That's it, UITableView gets a cell, places it in itself and that cell just lays there unchanged until rectangle stored in bounds property of UITableView ceases to include rectangle stored in the frame property of that cell. In that case UITableView marks that cell as hidden and places it into the pool of reusable cells.
Since the process of scrolling in essence is not a repositioning of subviews – it is merely a curious illusion of shifting a bounds viewport of UITableView – constant observing of UITableViewCell's properties are pointless.
Moreover, the frame property of subview of UITableViewCell also represents a position and size of that subview within its container, UITableViewCell. It is also will not change on scroll.
You need to observe changes in UITableView bounds property, which is also represented by contentOffset by the way. UITableView happens to be a subclass of UIScrollView, so you can use its delegate methods, such as -scrollViewDidScroll:, like in this simple example:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
UITableView *tableView = (UITableView *)scrollView;
// current position
CGFloat currentY = tableView.bounds.origin.y;
// current inset
CGFloat currentInset = tableView.contentInset.top;
// trigger line position
CGFloat triggerY = currentInset + currentY + kYourTriggerPosition;
// nice visual mark
UIView *line = [tableView viewWithTag:88];
if (!line) {
line = [[UIView alloc] initWithFrame:CGRectZero];
line.tag = 88;
line.backgroundColor = [UIColor redColor];
[tableView addSubview:line];
}
line.frame = CGRectMake(0, triggerY, tableView.bounds.size.width, 1);
// determine scroll direction
BOOL scrollingUp = currentY > self.previousY;
// all visible cells
NSArray *visibleCells = tableView.visibleCells;
[visibleCells enumerateObjectsUsingBlock:^(UITableViewCell *cell, NSUInteger idx, BOOL *stop) {
// subview
UIView *subview = [cell viewWithTag:kYourSubviewTag];
// subview frame rect in UITableView bounds
CGRect subviewRect = [subview convertRect:subview.frame toView:tableView];
// trigger line within subview?
BOOL triggered = (CGRectGetMinY(subviewRect) <= triggerY) && (CGRectGetMaxY(subviewRect) >= triggerY);
if (triggered) {
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
NSLog(#"moving %#, triggered for cell at [%2d:%2d]", #[#"down", #"up"][scrollingUp], indexPath.section, indexPath.row);
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}];
// save current position for future use
self.previousY = currentY;
}
Reach that subview of UITableViewCell with cellForRowAtIndexPath or tableView.visibleCells, then call convertRectToView: on that subview.
convertRectToView: allows you to do translations on different coordinate systems. For example, you can detect where that subview appears on screen by translating its frame within its superview into viewController.view
For more: Apple Documentation
Since I can not comment I am writing as an Answer
Changing the answer for the requirement.
Here is how I think it can be done, you need to have your custom UITableViewCell which has a function which can take in co-ordinates (again based on your logic if you just want an intersection where a cell just touches a boundary or if it has to be at a precise position in a frame), so your function would take the co-ordinates and will return a true and a false if it will tell you if the condition is met, and in your cellForTable function you call the function of UITableView cell to check if your condition is met, if it is in your view you create a subview at the exact location. You can also modify the function to return you the exact frame-cordinates so you can use them to create a subview\
Here's a simple approach, which you can use if you have only one section without section header.
Add this to your implementation:
CGFloat lastContentOffSet;
And then add this delegate method of scrollview as tableview is also a scrollview.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat cellHeight = 50;
CGFloat touchingPoint = 44.0f;
NSInteger rowNo = floor(scrollView.contentOffset.y / cellHeight);
NSInteger startPoint = (rowNo * cellHeight);
if (scrollView.contentOffset.y > lastContentOffSet) {
NSLog(#"Row %ld scrolled down", (long)rowNo);
if (scrollView.contentOffset.y > startPoint + touchingPoint) {
// Do something here
NSLog(#"Do something here");
}
}
else {
NSLog(#"Row %ld scrolled up", (long)rowNo);
if (scrollView.contentOffset.y > startPoint + touchingPoint) {
// Do something here
NSLog(#"Do something here");
}
}
lastContentOffSet = scrollView.contentOffset.y;
}
Change value of the cellheight according to your tableview cell and the distance of that subview with the cell.
Let me know if this code helped. :)

Explicitly set row height for some rows but use estimatedRowHeight for others

I have a UITableViewCell which contains a UIWebView as a subview. The web view fills the entire cell. I am using estimatedRowHeight to calculate the row height. However, at the time the table view is built, the cell has no height because the web view has not loaded it's content, therefore the cell has no content. Because of this, estimatedRowHeight returns 44 instead of the correct height of the web view content.
Does anyone know how I can correctly calculate the height of a row when the content is not immediately set? Is there a way to estimate the height of some cells and explicitly set the heigh of other cells, in the same table view?
This is how I am using estimatedRowHeight:
self.tableView.estimatedRowHeight = 200;
self.tableView.rowHeight = UITableViewAutomaticDimension;
I am not using the delegate method. I have tried it, but it does not change the result. The cell I am using has a xib which also uses Auto Layout constraints. To be clear, the cell does appear and the web view does get added to the cell. The problem is that the height of the cell is not big enough to show the entire web view. The web view loads an HTML embed code for an audio player.
I did some fiddling around and found out that you can use UITableViewAutomaticDimension in the following way:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableSection *tableSection = (self.tableModel.sections)[indexPath.section];
if (tableSection.sectionType == TableSectionTypeWebView)
{
return 120;
}
else
{
return UITableViewAutomaticDimension;
}
}
This basically says, use a height of 120 for any WebView sections but for everything else I want you to figure out the height. I am using a my own custom table model here (i.e. TableSection, sectionType, etc...)
I had to add self.tableView.estimatedRowHeight = 200; to my init method for that to work.
Now I can provide an estimated row height but also explicitly set a row height for some sections, or even some rows if I wanted.
I haven't seen any documentation for this, but I tested it with variable length strings and it held up just fine.
You make your class a UIWebViewDelegate and then in and set your class as a delegate to every single UIWebView in your UITableViewCell
-(void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
//Asks the view to calculate and return the size that best fits //its subviews.
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
Now you can get the height of the UIWebView and set it to the rows height, because the following method will be called again once a 'beginUpdates' is invoked
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath {
return _webView.frame.size.height;
}
Hope This helps

Using UIWebView in a UITableViewCell to display dynamic content

I need to display non-editable text with different styles and colors in table view cells. In researching, it seems that UIWebView is the way to achieve the style/color flexibility that I want. Here is what I did: (note iOS 5, XCode 4)
Created a class WebViewTableViewCell derived from UITableViewCell. The class contains a UIWebView.
In storyboard, I created the table view controller with 1 dynamic cell prototype containing a UIWebView object. The cell is of type WebViewTableViewCell. I made the connection between the class UIWebView and the storyboard.
In the table view controller code, overrode "heightForRowAtIndexPath" and used "sizeThatFits" to dynamically adjust the cell height
When I run, all of this "works", except that the cells do not resize to fit all of the text contained in the web view. In fact they do not resize at all; they are the height as drawn in the storyboard. Here is some sample code:
cellForRowAtIndexPath:
UITableViewCell *cell = nil;
WebViewTableViewCell *webCell = (WebViewTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"cellid"];
// Now populate the cell with dynamic data
NSDictionary *thisData = [self.dynamicData objectAtIndex:indexPath.row];
NSString *myString= [NSString stringWithFormat:#"%#. %#",
[thisData objectForKey:#"key1"],
[thisData objectForKey:#"key2"]];
[webCell.webView loadHTMLString:myStringbaseURL:nil];
cell = webCell;
return cell;
heightForRowAtIndexPath:
WebViewTableViewCell *webCell = (WebViewTableViewCell *) [self tableView:a_tableView cellForRowAtIndexPath:indexPath];
// Now that we have the cell, let it determine what its height should be.
CGFloat rowHeight = 45; // default unless if a problem accessing the cell
if( webCell )
{
CGSize size = [webCell.webView sizeThatFits:CGSizeZero];
rowHeight = rowHeight < size.height ? size.height : rowHeight;
}
One more thing - the cells, although not the correct height, are scrollable. I want to make them a fixed height and not scrollable.
Any thoughts?
--John
To disable scrolling you can just set tableView.scrollEnabled = NO. To get the correct height you can set your view controller as the webview delegate, and do something like this:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *result = [webView stringByEvaluatingJavaScriptFromString:#"var height = 0; for (var i=0; i < document.all.length; i++) { document.all[i].height; if (document.all[i].height > height) { height = document.all[i].height; } }"];
CGFloat height = [result floatValue];
}
I'm sorry about the big JavaScript, but I think it will work. However, I recommend you to use the built in UILabel that supports NSAttributedString, or an open source UILabel subclass if you support iOS versions prior to 6.
Here is a better aproach to custom tableview.

How to set the width of a cell in a UITableView in grouped style

I have been working on this for about 2 days, so i thought i share my learnings with you.
The question is: Is it possible to make the width of a cell in a grouped UITableView smaller?
The answer is: No.
But there are two ways you can get around this problem.
Solution #1: A thinner table
It is possible to change the frame of the tableView, so that the table will be smaller. This will result in UITableView rendering the cell inside with the reduced width.
A solution for this can look like this:
-(void)viewWillAppear:(BOOL)animated
{
CGFloat tableBorderLeft = 20;
CGFloat tableBorderRight = 20;
CGRect tableRect = self.view.frame;
tableRect.origin.x += tableBorderLeft; // make the table begin a few pixels right from its origin
tableRect.size.width -= tableBorderLeft + tableBorderRight; // reduce the width of the table
tableView.frame = tableRect;
}
Solution #2: Having cells rendered by images
This solution is described here: http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html
I hope this information is helpful to you. It took me about 2 days to try a lot of possibilities. This is what was left.
A better and cleaner way to achieve this is subclassing UITableViewCell and overriding its -setFrame: method like this:
- (void)setFrame:(CGRect)frame {
frame.origin.x += inset;
frame.size.width -= 2 * inset;
[super setFrame:frame];
}
Why is it better? Because the other two are worse.
Adjust table view width in -viewWillAppear:
First of all, this is unreliable, the superview or parent view controller may adjust table view frame further after -viewWillAppear: is called. Of course, you can subclass and override -setFrame: for your UITableView just like what I do here for UITableViewCells. However, subclassing UITableViewCells is a much common, light, and Apple way.
Secondly, if your UITableView have backgroundView, you don't want its backgroundView be narrowed down together. Keeping backgroundView width while narrow down UITableView width is not trivial work, not to mention that expanding subviews beyond its superview is not a very elegant thing to do in the first place.
Custom cell rendering to fake a narrower width
To do this, you have to prepare special background images with horizontal margins, and you have to layout subviews of cells yourself to accommodate the margins.
In comparison, if you simply adjust the width of the whole cell, autoresizing will do all the works for you.
To do this in Swift, which does not provide methods to set variables, you'll have to override the setter for frame. Originally posted (at least where I found it) here
override var frame: CGRect {
get {
return super.frame
}
set (newFrame) {
let inset: CGFloat = 15
var frame = newFrame
frame.origin.x += inset
frame.size.width -= 2 * inset
super.frame = frame
}
}
If nothing works you can try this
Make the background colour of the cell as clear color and then put an image of the cell with required size. If you want to display some text on that cell put a label above the image. Don't forget to set the background color of the label also to clear color.
I found the accepted solution didn't work upon rotation. To achieve UITableViewCells with fixed widths & flexible margins I just adapted the above solution to the following:
- (void)setFrame:(CGRect)frame {
if (self.superview) {
float cellWidth = 500.0;
frame.origin.x = (self.superview.frame.size.width - cellWidth) / 2;
frame.size.width = cellWidth;
}
[super setFrame:frame];
}
The method gets called whenever the device rotates, so the cells will always be centered.
There is a method that is called when the screen is rotated : viewWillTransitionToSize
This is where you should resize the frame. See example. Change the frame coords as you need to.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
int x = 0;
int y = 0;
self.tableView.frame = CGRectMake(x, y, 320, self.tableView.frame.size.height);
}];
}
i do it in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
CGFloat tableBorderLeft = self.view.frame.origin.x + 10;
CGFloat tableBorderRight = self.view.frame.size.width - 20;
CGRect tableRect = self.view.frame;
tableRect.origin.x = tableBorderLeft;
tableRect.size.width = tableBorderRight;
tableView.frame = tableRect;
}
And this worked for me
In .h file add the delegate 'UITableViewDataSource'
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return size;
}

Resources