I have used a UIScroll View. It has a contentView (of type UIView) which again has a questionView (UIView) and a _questionTableView (UITableView). I know this is not the right practice but the design required me to implement like this. Scroll is working perfectly fine.
I have a total of 8 cells and only 4 cells are visible when the screen is opened first. Upon tapping on any of these 4 cells
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
is called.
Upon scrolling down, rest of the cells become visible. But tapping on them doesn't trigger didSelectRowAtIndexPath
I read various stack overflow posts on this issue (to enable tapping on cells) where people suggested to increase the content view size/ scrollView's contentSize/ tableView's height. I tried all of them but no luck.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"myCell"];
_scrollView.contentSize = CGSizeMake(_questionTableView.frame.size.width, 1000);
_contentView.frame = CGRectMake(0, 0, _questionTableView.frame.size.width * 2, 1000);
_questionTableView.frame = CGRectMake(0, 0, _qTableView.frame.size.width , 500);
self.scrollView.delegate = self;
return cell;
}
Is there nay way to make the cells tappable?
EDIT: I experimented some more and I realized that after several taps on the cell, the method - didSelectRowAtIndexPath is called. But those are some random taps 5-6 times. Couldn't identify a pattern.
Try to deselect property "Delays Content Touches" in your parent scroll view. It may help!
Related
I am trying to have some controls (labels, inputs) and TableView in ScrollView (I have table at bottom). So when user is scrolling that first there are hidden controls at top and then are shown items at their place and then first items are hidden and next are shown etc.
I created this code:
if(self.searchResultsTableView.frame.size.height != self.searchResultsTableView.contentSize.height)
{
CGRect newFrame = self.searchResultsTableView.frame;
newFrame.size = self.searchResultsTableView.contentSize;
self.searchResultsTableView.frame = newFrame;
mainScrollView.contentSize = CGSizeMake(320, self.searchResultsTableView.frame.origin.y + newFrame.size.height);
}
in this method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Height is calculated right and it's working but I have problem with some cells. It shows just cell which are loaded and visible without scrolling and when I am trying to scroll I am scrolling but next cells aren't visible. I must tap a few times on button to reload cells to get them visible and then I see them and everything works.
I guess this could be problem because some lazy loading or something like that. Anyone can help me? Thanks
Try this
set your tableview scrolling disable
and set your scrollview content size as your table height.
Try use willDisplayCell instead cellForRow.
I have a standard UINavigationController with a UITableViewController at it's root. In IB, I paint a prototype cell with a label and a UIView. The UIView contains a button. I'd like the UIView to be x-aligned after the label, as a function of the length of text in the label.
In IB, the view's left side is initially aligned with the label's left side. There are no layout constraints in IB.
Here's my cellForRowAtIndexPath ...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSArray *labels = #[#"SOME STRING", #"SHORTER", #"A VERY MUCH LONGER ONE", #"REGULAR ONE", #"TINY"];
UILabel *label = (UILabel *)[cell viewWithTag:32];
label.text = labels[indexPath.row];
UIView *view = [cell viewWithTag:33];
CGSize size = [label.text sizeWithAttributes:#{NSFontAttributeName:label.font}];
view.frame = CGRectOffset(label.frame, size.width, 0);
return cell;
}
Two problems: I've set breakpoints and watch this code running the first time the view appears. I see the view.frame get changed for each row, but the view does not change position. I remains in it's IB-position, right on top of the label. If I scroll the table down, the views on lower cells (presumably reused) are in the desired position. If I scroll back up, the upper rows are also good. It just fails to work on the initial presentation of the upper cells.
Second problem is that the button contained in only the first row has a subtle, strange effect applied to it's text, like a blur. See attached...
First Row Button (zoomed in mac preview... see that extra blur on the left edge of the letters?)
Other Row Buttons
Stuff I tried:
I've tried a few variations, including using a regular view controller with a table view added (rather than a UITableViewController). I've found that if I reloadData on viewDidAppear, that solves the placement problem, but not the blurry button. (Also, I don't like the idea of needing to reload on viewDidAppear). Doing so on viewWillAppear has no effect at all. I've also tried animating the label change slowly. It happens, but again, only on the second time the cell is configured. I try changing the UIView color to prove the code is being run. The color change happens every time, including the first time, but not the view placement. Am I nuts?
For problem 1:
Try calling [cell layoutIfNeeded] before returning the cell, there should be no performance hit when it does not need relayout.
For problem 2:
try calling CGRectIntegral before you set the frame. ie
view.frame = CGRectIntegral( CGRectOffset(label.frame, size.width, 0) );
My table has cells which have several labels. I want one of these labels to fit its size so text begins right below the Title (remember that Labels align text vertically unless you fit its container).
Problem is, the very first time the table is loaded all labels' texts are succesfully populated but label sizes don't actually graphically apply until the NEXT time a refresh is asked. (if I ask for a reloadData with the exact same information, the labels' sizes work flawlessly).
This is some of my cellForRowAtIndexPath code:
cell.body.text = user.message;
[cell.body sizeToFit];
The only solution I've found so far is double calling [table reloadData] but this is an ugly solution. Any way I can fix this?
Edit: Previous code was a summary, I'll show the whole code here as requested:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TwitterTweetCell *cell = [tableView dequeueReusableCellWithIdentifier:#"TwitterTweetCell"];
// Populate cell
TweetModelData *tweet = [self.twitterModelData.tweets objectAtIndex:[indexPath item]];
cell.tweetName.text = tweet.user;
cell.tweetChannel.text = tweet.userName;
cell.tweetBody.text = tweet.message;
[cell.tweetBody sizeToFit];
return cell;
}
Regarding cell size, everything is working ok. Depending on the size of the message each cell has a different size which was pre-calculated before.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return ((TweetModelData *)[self.twitterModelData.tweets objectAtIndex:[indexPath item]]).tweetHeight + 30.0f;
}
I finally found it. Don't know the reason, but it seems disabling "Use autolayout" on my storyboard fixed it.
I'm guessing auto-layout was overwriting the layout changes I was applying so they had no effect until the next data reload.
I have a custom tableFooterView (it's just an 8px high CoreGraphics arc with a gradient) that I set with the tableFooterView property in viewDidLoad rather than viewForFooterInSection. When setting it with viewForFooterInSection, it floated over the content when it reached the bottom, whereas tableFooterView does what I want it to in that it stays with the UITableView's height.
But when the cells or table view do change in height, the tableFooterView animates to them slowly (about half a second but it's very noticeable). This is pretty awkward since the footer is supposed to look like an extension of the last cell. For instance, when heightForRowAtIndexPath changes the height of a cell, the tableFooterView kind of ghost-floats back. In this screenshot the bottom cell has just been shrunken to its normal size and the footer is floating back.
(As a new user I can't post images but here's the link: http://desmond.imageshack.us/Himg835/scaled.php?server=835&filename=iossimulatorscreenshotj.png&res=landing)
(This content is no longer available, 14/9/15).
It will also float over the content when the height of the last cell is suddenly changed to be larger than it was.
Any pointers? Thanks very much.
Edit: By cells changing in height, I mean through the heightForRowAtIndexPath section:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
Note *currentNote = [self.notes objectAtIndex:indexPath.row];
if (currentNote.associatedCellIsSelected) {
return NORMAL_CELL_FINISHING_HEIGHT*2;
}
return NORMAL_CELL_FINISHING_HEIGHT;
}
Edit 2: In didSelectRowAtIndexPath I make the cell selected (actually the cell's note), begin / end updates as well as call reloadRow for the row that's been selected.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Note *currentNote = [self.notes objectAtIndex:indexPath.row];
if (currentNote.associatedCellIsSelected == FALSE) {
currentNote.associatedCellIsSelected = TRUE;
[tableView beginUpdates];
[tableView endUpdates];
}
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; }
I've also made sure that I get the same behavior with a plain rect redColor UIView in place of the Core Graphics footer, with the same results. I wish there was just an easy way to override the footer and tell it to not animate!
I am trying to resize my UITableViewCell's frame via:
[cell setFrame:CGRectMake(cell.frame.origin.x,
cell.frame.origin.y,
cell.frame.size.width,
cell.frame.size.height+25)];
however, it's not resizing after I do this... why is this?
This is weird as if I add a UIToolBar into the cell, it resizes but when I am adding a UIView, it doesn't:
[cell.contentView addSubview:sideSwipeView];
Here's the long and short of it:
Your cell width is determined by the width of the tableview it's in.
[EDIT: If it's a grouped table view, the cell is 20 - 60 pixels narrower than the tableview width, depending if you're using an iPhone, or an iPad.]
Your cell height is determined by the heightForRowAtIndexPath method.
If you're manually setting the cell's frame, it's going to be useless except when you're using a subclassed cell where you want to add subviews based on the cell's dimensions.
Even in this case, it's recommended to get the cell's frame from the tableview by using rectForRowAtIndexPath:(NSIndexPath*)indexPath method and then setting that frame as the cell's frame (after setting the frame's origin Y as 0).
I'm not quite sure about the UIToolBar, but your subview's frame won't change on changing the cell frame.
Maybe if you could tell us what you're trying to achieve, we can suggest a solution for you?
--------------------EDIT--------------------
So you need to dynamically add a subview to a cell on tapping it and resize it's height according to the new subview. This is gonna get hairy so here goes:
In your .h file declare:
BOOL subviewAdded;
In your .m file, in the init, do:
subviewAdded = NO;
Let's assume that you want the cell's height to be 50 without the subview and 100 with the subview. Accordingly, your heightForRow method should be:
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return (subviewAdded?100.0f:50.0f);
}
This means that initially since subviewAdded is NO, all your cells will have the smaller height.
Now, to add a subview to a cell on tapping it, and to change it's height dynamically, do this in your didSelectRow method:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the cell at this indexPath
UITableViewCell *thisCell = [tableView cellForRowAtIndexPath:indexPath];
if(subviewAdded)
{
subviewAdded = NO;
for(int i = 0; i < [thisCell.contentView.subviews count]; i++)
{
UIView *thisSubview = [thisCell.contentView.subviews objectAtIndex:i];
[thisSubview removeFromSuperview];
}
}
else
{
UIView *someView = [[UIView alloc] initWithFrame:someFrame];
[thisCell.contentView addSubview:someView];
[someView release];
subviewAdded = YES;
}
NSMutableArray *array = [NSMutableArray array];
[array addObject:indexPath];
[tableView reloadRowsAtIndexPaths:array
withRowAnimation:UITableViewRowAnimationFade];
}
So what's going to happen here is you're adding a subview to this cell you've tapped. Reloading this cell will call heightForRowAtIndexPath and do a nice little fade animation and change your tableview heights.
IMPORTANT: Ideally, you should maintain an array of NSNumbers with boolean values. The array size should be the same size as the number of tableview cells you have.
In heightForRow, you would then check against this array instead of using a single boolean for the whole tableView. This would ensure that you could have different heights for different cells.
That would look something like:
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL thisBool = (BOOL)[[booleanArray objectAtIndex:indexPath.row] boolValue];
return (thisBool?100.0f:50.0f);
}
I didn't post all that code here since it's implied and what I've posted should put you well on your way to doing the boolean array thing.
Anyway, there you are. I just tested this code myself so it works :)
If you want to increase the height of your cell based on some parameter eg. text, image,
you must implement the heightForRowAtIndexPath method of UITableViewDelegate in your code.
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath