In our app the user is able in a intuitive way to scroll to next section in the tableview using some controls outside the tableview. Some sections contains many cells and scrolling animated does not look smooth because there is just too many cells to scroll by. For the sake of a simple and understood animation we want to temporarily remove the cells which are excessive for the animation.
Say the user is on
section.0 row.5 out of 100 rows
and he want to scroll to
section.1 row.0 out of 100 rows
Then we want to sort of skip all the excessive cells while scrolling animated. So we temporarily want to remove all cells between
e.g. section.0 row.10 untill section.0 row.98
Any ideas how I can get by this? I'm sure this could be usefull to others as well. I want to do this as clean as possible.
I have a few ideas on how you might be able to handle this. First is to reload the cells of interest, and return a lightweight cell. You might be able to use CGBitmapContext to copy the image data into a "facade" cell instead of the real one. Second would be to reload the data for the UITableView and then not return the data for the rows of interest. Third is to actually remove the rows. Another idea might be to disable interaction while you're animating.
Reload rows:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPathOfYourCell, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
Insert/Delete rows:
[tableView beginUpdate];
[tableView insertRowsAtIndexPaths:*arrayOfIndexPaths* withRowAnimation:*rowAnimation*];
[tableView endUpdate];
[tableView beginUpdate];
[tableView deleteRowsAtIndexPaths:*arrayOfIndexPaths* withRowAnimation:*rowAnimation*];
[tableView endUpdate];
Disable Interaction:
[UIApplication sharedApplication] beginIgnoringInteractionEvents];
This is an early attempt. I feel this is a bit messy..
Self is subclass of UITableView
- (void)scrollAndSkipCellsAnimatedToTopOfSection:(NSUInteger)section
{
CGRect sectionRect = [self rectForHeaderInSection:section];
CGPoint targetPoint = sectionRect.origin;
CGFloat yOffsetDiff = targetPoint.y - self.contentOffset.y;
BOOL willScrollUpwards = yOffsetDiff > 0;
if(willScrollUpwards)
{
[self scrollAndSkipCellsAnimatedUpwardsWithDistance:fabs(yOffsetDiff)];
}
else
{
[self scrollAndSkipCellsAnimatedDownwardsWithDistance:fabs(yOffsetDiff)];
}
}
- (void)scrollAndSkipCellsAnimatedUpwardsWithDistance:(CGFloat)distance
{
// when going upwards contentOffset should decrease
CGRect rectToRemove = CGRectMake(0,
self.contentOffset.y + (self.bounds.size.height * 1.5) - distance,
self.bounds.size.width,
distance - (self.bounds.size.height * 2.5));
BOOL shouldRemoveAnyCells = rectToRemove.size.height > 0;
if(shouldRemoveAnyCells)
{
// property on my subclass of uitableview
// these indexes may span over several sections
self.tempRemoveIndexPaths = [self indexPathsForRowsInRect:rectToRemove];
}
[UIView setAnimationsEnabled:NO];
[self beginUpdates];
[self deleteRowsAtIndexPaths:self.tempRemoveIndexPaths withRowAnimation:UITableViewRowAnimationNone];
[self endUpdates];
[UIView setAnimationsEnabled:YES];
[self setContentOffset:CGPointMake(0, self.contentOffset.y - distance) animated:YES];
}
// And then I would probably have to put some logic into
// - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (void)scrollAndSkipCellsAnimatedDownwardsWithDistance:(CGFloat)distance
{
}
I am using Xcode's SplitView template.
I have placed a bunch of text fields on the 'detail' side of the view however I forgot about the keyboard taking up half the screen.
How do I move this detail view up when the keyboard comes onto the screen?
I used this solution from the always-useful Cocoa With Love when I could not use a UIScrollView.
If you want to add a UIScrollView you your hierarchy, it gets even easier. Just insert the UIScrollview in the hierarchy above the text fields and use this code when the user taps the text item to begin editing:
UIScrollView* v = (UIScrollView*) self.view ;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:v];
rc.origin.x = 0 ;
rc.origin.y -= 60 ;
rc.size.height = 400;
[self.scroll scrollRectToVisible:rc animated:YES];
Good Luck!
I had that problem once. My app used a table view to show the fields, so the solution I came up with is to listen the textViewDidBeginEditing to perform a scrollToRowAtIndexPath on the table view:
- (void)textViewDidBeginEditing:(UITextView *)textView{
UITableViewCell *cell = (UITableViewCell*) [[textView superview] superview];
[self.myTableView scrollToRowAtIndexPath:[self.myTableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
If you don't have a table view but a scroll view I think you could take same similar approach but telling the scrollview to scroll to the section you need just as #MystikSpiral told.
I have a tableView that I'm inserting rows into at the top.
Whilst I'm doing this I want the current view to stay completely still, so the rows only appear if you scroll back up.
I've tried saving the current position of the underlying UIScrollview and resetting the position after the rows have been inserted but this results in a judder, up and down, although it does end up back in the same place.
Is there a good way of achieving this ?
Update: I am using beginUpdate, then insertRowsAtIndexPath, endUpdates. There is no reloadData call.
scrollToRowAtIndexPath jumps to the top of the current cell (saved before adding rows).
The other approach I tried, which ends up in exactly the right pace, but with a judder is.
save tableView currentOffset. (Underlying scrollView method)
Add rows (beginUpdates,insert...,endUpdates)
reloadData ( to force a recalulation of the scrollview size )
Recalculate the correct new offset from the bottom of the scrollview
setContentOffset (Underlying scrollview method)
Trouble is the reloadData causes the scrollview/tableview to start scrolling briefly, then the setContentOffset returns it to the correct place.
Is there a way of getting a tableView to work out it's new size without starting display ?
Wrapping the whole thing in a beginAnimation commitAnimation doesn't help much either.
Update 2: This can clearly be done - see the offical twitter app for one when you pull down for updates.
There's really no need to sum up all rows height,
the new contentSize after reloading the table is already representing that.
So all you have to do is calculate the delta of contentSize height and add it to the current offset.
...
CGSize beforeContentSize = self.tableView.contentSize;
[self.tableView reloadData];
CGSize afterContentSize = self.tableView.contentSize;
CGPoint afterContentOffset = self.tableView.contentOffset;
CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + afterContentSize.height - beforeContentSize.height);
self.tableView.contentOffset = newContentOffset;
...
-(void) updateTableWithNewRowCount : (int) rowCount
{
//Save the tableview content offset
CGPoint tableViewOffset = [self.tableView contentOffset];
//Turn of animations for the update block
//to get the effect of adding rows on top of TableView
[UIView setAnimationsEnabled:NO];
[self.tableView beginUpdates];
NSMutableArray *rowsInsertIndexPath = [[NSMutableArray alloc] init];
int heightForNewRows = 0;
for (NSInteger i = 0; i < rowCount; i++) {
NSIndexPath *tempIndexPath = [NSIndexPath indexPathForRow:i inSection:SECTION_TO_INSERT];
[rowsInsertIndexPath addObject:tempIndexPath];
heightForNewRows = heightForNewRows + [self heightForCellAtIndexPath:tempIndexPath];
}
[self.tableView insertRowsAtIndexPaths:rowsInsertIndexPath withRowAnimation:UITableViewRowAnimationNone];
tableViewOffset.y += heightForNewRows;
[self.tableView endUpdates];
[UIView setAnimationsEnabled:YES];
[self.tableView setContentOffset:tableViewOffset animated:NO];
}
-(int) heightForCellAtIndexPath: (NSIndexPath *) indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
int cellHeight = cell.frame.size.height;
return cellHeight;
}
Simply pass in the row count of the new rows to insert at the top.
#Dean's way of using an image cache is too hacky and I think it destroys the responsiveness of the UI.
One proper way:
Use a UITableView subclass and override -setContentSize: in which you can by some means calculate how much the table view is pushed down and offset that by setting contentOffset.
This is a simplest sample code to handle the simplest situation where all insertions happen at the top of table view:
#implementation MyTableView
- (void)setContentSize:(CGSize)contentSize {
// I don't want move the table view during its initial loading of content.
if (!CGSizeEqualToSize(self.contentSize, CGSizeZero)) {
if (contentSize.height > self.contentSize.height) {
CGPoint offset = self.contentOffset;
offset.y += (contentSize.height - self.contentSize.height);
self.contentOffset = offset;
}
}
[super setContentSize:contentSize];
}
#end
had the same problem and found a solution.
save tableView currentOffset. (Underlying scrollView method)
//Add rows (beginUpdates,insert...,endUpdates) // don't do this!
reloadData ( to force a recalulation of the scrollview size )
add newly inserted row heights to contentOffset.y here, using tableView:heightForRowAtIndexPath:
setContentOffset (Underlying scrollview method)
like this:
- (CGFloat) firstRowHeight
{
return [self tableView:[self tableView] heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}
...
CGPoint offset = [[self tableView] contentOffset];
[self tableView] reloadData];
offset.y += [self firstRowHeight];
if (offset.y > [[self tableView] contentSize].height) {
offset.y = 0;
}
[[self tableView] setContentOffset:offset];
...
works perfectly, without glitches.
I did some testing with a core data sample project and got it to sit still while new cells were added above the top visible cell. This code would need adjustment for tables with empty space on the screen, but once the screen is filled, it works fine.
static CGPoint delayOffset = {0.0};
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
if ( animateChanges )
[self.tableView beginUpdates];
delayOffset = self.tableView.contentOffset; // get the current scroll setting
}
Added this at cell insertion points. You may make counterpart subtraction for cell deletion.
case NSFetchedResultsChangeInsert:
delayOffset.y += self.tableView.rowHeight; // add for each new row
if ( animateChanges )
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
and finally
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
if ( animateChanges )
{
[self.tableView setContentOffset:delayOffset animated:YES];
[self.tableView endUpdates];
}
else
{
[self.tableView reloadData];
[self.tableView setContentOffset:delayOffset animated:NO];
}
}
With animateChanges = NO, I could not see anything move when cells were added.
In testing with animateChanges = YES, the "judder" was there. It seems the animation of cell insertion did not have the same speed as the animated table scrolling. While the result at the end could end with visible cells exactly where they started, the whole table appears to move 2 or 3 pixels, then move back.
If the animation speeds could be make to equal, it may appear to stay put.
However, when I pressed the button to add rows before the previous animation finished, it would abruptly stop the animation and start the next, making an abrupt change of position.
#Dean,
You can change your code like this to prevent animating.
[tableView beginUpdates];
[UIView setAnimationsEnabled:NO];
// ...
[tableView endUpdates];
[tableView setContentOffset:newOffset animated:NO];
[UIView setAnimationsEnabled:YES];
Everyone loves copy and pasting code examples, so here's an implementation of Andrey Z.'s answer.
This is in my delegateDidFinishUpdating:(MyDataSourceDelegate*)delegate method
if (self.contentOffset.y <= 0)
{
[self beginUpdates];
[self insertRowsAtIndexPaths:insertedIndexPaths withRowAnimation:insertAnimation];
[self endUpdates];
}
else
{
CGPoint newContentOffset = self.contentOffset;
[self reloadData];
for (NSIndexPath *indexPath in insertedIndexPaths)
newContentOffset.y += [self.delegate tableView:self heightForRowAtIndexPath:indexPath];
[self setContentOffset:newContentOffset];
NSLog(#"New data at top of table view");
}
The NSLog at the bottom can be replaced with a call to show a view that indicated there's fresh data.
I faced situation where there are many sections which may have different row count between -reloadData calls because of custom grouping, and row heights vary. So here is solution based on AndreyZ's. It contentHeight property of UIScrollView before and after -reloadData and it seems like more universal.
CGFloat contentHeight = self.tableView.contentSize.height;
CGPoint offset = self.tableView.contentOffset;
[self.tableView reloadData];
offset.y += (self.tableView.contentSize.height - contentHeight);
if (offset.y > [self.tableView contentSize].height)
offset.y = 0;
[self.tableView setContentOffset:offset];
I want add additional condition.
If your code in iOS11 or more, you need do like below;
In iOS 11, table views use estimated heights by default. This means that the contentSize is just as estimated value initially. If you need to use the contentSize, you’ll want to disable estimated heights by setting the 3 estimated height properties to zero:
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0 tableView.estimatedSectionFooterHeight = 0
How are you adding the rows to the table?
If you're changing the data source and then calling reloadData, that may result in the table being scrolled to the top again.
However, if you use the beginUpdates, insertRowsAtIndexPaths:withRowAnimation:, endUpdates methods, you should be able to insert rows without having to call reloadData thus keeping the table in its original position.
Don't forget to modify your data source before calling endUpdates or else you'll end up with an internal inconsistency exception.
You don't need to do so much difficult operations, furthermore these manipulations wouldn't work perfectly. The simple solution is to rotate table view, and then rotate cells into it.
tableView.transform = CGAffineTransformMakeRotation(M_PI);
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.transform = CGAffineTransformMakeRotation(M_PI);
}
Use [tableView setScrollIndicatorInsets:UIEdgeInsetsMake(0, 0, 0, 310)] to set relative position to scroll indicator. It will be on the right side after you table view rotation.
Just a heads up it does not seem possible to do this if you return estimated heights for the tableview.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath ;
If you implement this method and return a rough height your tableview will jump about when reloading as it appears to use these heights when setting the offsets.
To get it working use one of the above answers (I went with #Mayank Yadav answer), don't implement the estimatedHeight method and cache the cell heights (remembering to adjust the cache when you insert additional cells at the top).
Simple solution to disable animations
func addNewRows(indexPaths: [NSIndexPath]) {
let addBlock = { () -> Void in
self.tableView.beginUpdates()
self.tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .None)
self.tableView.endUpdates()
}
tableView.contentOffset.y >= tableView.height() ? UIView.performWithoutAnimation(addBlock) : addBlock()
}
Late to the party but this works even when cell have dynamic heights (a.k.a. UITableViewAutomaticDimension), no need to iterate over cells to calculate their size, but works only when items are added at the very beginning of the tableView and there is no header, with a little bit of math it's probably possible to adapt this to every situation:
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
self.getMoreMessages()
}
}
private func getMoreMessages(){
var initialOffset = self.tableView.contentOffset.y
self.tableView.reloadData()
//#numberOfCellsAdded: number of items added at top of the table
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: numberOfCellsAdded, inSection: 0), atScrollPosition: .Top, animated: false)
self.tableView.contentOffset.y += initialOffset
}
I solved this in the end by rendering the current tableview into a UIImage and then putting a temporary UIImageView over the tableview whilst it animates.
The following code will generate the image
// Save the current tableView as an UIImage
CSize pageSize = [[self tableView] frame].size;
UIGraphicsBeginImageContextWithOptions(pageSize, YES, 0.0); // 0.0 means scale appropriate for device ( retina or no )
CGContextRef resizedContext = UIGraphicsGetCurrentContext();
CGPoint offset = [[self tableView] contentOffset];
CGContextTranslateCTM(resizedContext,-(offset.x),-(offset.y));
[[[self tableView ]layer] renderInContext:resizedContext];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You need to keep track of how much the tableview will have grown by whilst inserting rows and make sure you scroll the tableview back to the exact same position.
Based on Andrey Z's answer, here is a live example working perfect for me...
int numberOfRowsBeforeUpdate = [controller.tableView numberOfRowsInSection:0];
CGPoint currentOffset = controller.tableView.contentOffset;
if(numberOfRowsBeforeUpdate>0)
{
[controller.tableView reloadData];
int numberOfRowsAfterUpdate = [controller.tableView numberOfRowsInSection:0];
float rowHeight = [controller getTableViewCellHeight]; //custom method in my controller
float offset = (numberOfRowsAfterUpdate-numberOfRowsBeforeUpdate)*rowHeight;
if(offset>0)
{
currentOffset.y = currentOffset.y+offset;
[controller.tableView setContentOffset:currentOffset];
}
}
else
[controller.tableView reloadData];
AmitP answers, Swift 3 version
let beforeContentSize = self.tableView.contentSize
self.tableView.reloadData()
let afterContentSize = self.tableView.contentSize
let afterContentOffset = self.tableView.contentOffset
let newContentOffset = CGPoint(x: afterContentOffset.x, y: afterContentOffset.y + afterContentSize.height - beforeContentSize.height)
self.tableView.contentOffset = newContentOffset
How about using scrollToRowAtIndexPath:atScrollPosition:animated:? You should be able to just add an element to your data source, set the row with the above mentioned method and reload the table...
I have implemented a custom UITableViewCell which includes a UITextView that auto-resizes as the user types, similar to the "Notes" field in the Contacts app. It is working properly on my iPhone, but when I am testing it in the iPad, I am getting some very strange behavior: When you get to the end of a line, the keyboard hides for a millisecond and then shows itself again immediately. I would write it off as just a quirky bug, but it actually causes some data loss since if you are typing, it loses a character or two. Here's my code:
The Code
// returns the proper height/size for the UITextView based on the string it contains.
// If no string, it assumes a space so that it will always have one line.
- (CGSize)textViewSize:(UITextView*)textView {
float fudgeFactor = 16.0;
CGSize tallerSize = CGSizeMake(textView.frame.size.width-fudgeFactor, kMaxFieldHeight);
NSString *testString = #" ";
if ([textView.text length] > 0) {
testString = textView.text;
}
CGSize stringSize = [testString sizeWithFont:textView.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
return stringSize;
}
// based on the proper text view size, sets the UITextView's frame
- (void) setTextViewSize:(UITextView*)textView {
CGSize stringSize = [self textViewSize:textView];
if (stringSize.height != textView.frame.size.height) {
[textView setFrame:CGRectMake(textView.frame.origin.x,
textView.frame.origin.y,
textView.frame.size.width,
stringSize.height+10)]; // +10 to allow for the space above the text itself
}
}
// as per: https://stackoverflow.com/questions/3749746/uitextview-in-a-uitableviewcell-smooth-auto-resize
- (void)textViewDidChange:(UITextView *)textView {
[self setTextViewSize:textView]; // set proper text view size
UIView *contentView = textView.superview;
// (1) the padding above and below the UITextView should each be 6px, so UITextView's
// height + 12 should equal the height of the UITableViewCell
// (2) if they are not equal, then update the height of the UITableViewCell
if ((textView.frame.size.height + 12.0f) != contentView.frame.size.height) {
[myTableView beginUpdates];
[myTableView endUpdates];
[contentView setFrame:CGRectMake(0,
0,
contentView.frame.size.width,
(textView.frame.size.height+12.0f))];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int height;
UITextView *textView = myTextView;
[self setTextViewSize:textView];
height = textView.frame.size.height + 12;
if (height < 44) { // minimum height of 44
height = 44;
[textView setFrame:CGRectMake(textView.frame.origin.x,
textView.frame.origin.y,
textView.frame.size.width,
44-12)];
}
return (CGFloat)height;
}
The Problems
So, here's what's happening
This code is working 100% properly on my iPhone and in the iPhone simulator. As I type the text, the UITextView grows smoothly, and the UITableViewCell along with it.
On the iPad simulator, however, it gets screwy. It works fine while you are typing on the first line, but when you get to the end of a line, the keyboard disappears and then reappears immediately, so that if the user continues typing the app misses a character or two.
Here are some additional notes on the weird behaviors that I have noticed which may help explain it:
Also, I have found that removing the lines [myTableView beginUpdates]; [myTableView endUpdates]; in the function textViewDidChange:(UITextView *)textView makes the UITextView grow properly and also doesn't show and hide the keyboard, but unfortunately, then the UITableViewCell doesn't grow to the proper height.
UPDATE: Following these instructions, I am now able to stop the strange movement of the text; but the keyboard is still hiding and showing, which is very strange.
Does anyone have any ideas as to how to get the keyboard to continually show, rather than hide and show when you get to the end of the line on the iPad?
P.S.: I am not interested in using ThreeTwenty.
you should return NO in:
-(BOOL) textViewShouldEndEditing:(UITextView *)textView
if you would like to show keyboard at all times. You should handle cases, which keyboard should be hidden, by returning YES to this delegate function.
edit:
I dug a little more, when [tableView endUpdates] called, it basically does 3 things :
Disables user interaction on the tableView
Updates cell changes
Enables user interaction on the tableView
The difference between SDKs(platforms) is at [UIView setUserInteractionEnabled] method. As UITableView does not overrite setUserInteractionEnabled method, it is called from super (UIView).
iPhone when setUserInteractionEnabled called, looks for a private field _shouldResignFirstResponderWithInteractionDisabled which returns NO as default, so does not resign the first responder (UITextView)
But on iPad there is no such check AFAIK, so it resignes UITextView on step 1, and sets focus and makes it first responder on step 3
Basically, textViewShouldEndEditing, which allows you to keep focus, according to SDK docs, is your only option ATM.
This method is called when the text
view is asked to resign the first
responder status. This might occur
when the user tries to change the
editing focus to another control.
Before the focus actually changes,
however, the text view calls this
method to give your delegate a chance
to decide whether it should.
I had the same issue for an iPad app and came up with another solution without having calculating the height of the text itself.
First create a custom UITableViewCell in IB with an UITextField placed in the cell's contentView. It's important to set the text view's scrollEnabled to NO and the autoresizingMask to flexibleWidth and flexibleHeight.
In the ViewController implement the text view's delegate method -textViewDidChanged: as followed, where textHeight is a instance variable with type CGFloat and -tableViewNeedsToUpdateHeight is a custom method we will define in the next step.
- (void)textViewDidChange:(UITextView *)textView
{
CGFloat newTextHeight = [textView contentSize].height;
if (newTextHeight != textHeight)
{
textHeight = newTextHeight;
[self tableViewNeedsToUpdateHeight];
}
}
The method -tableViewNeedsToUpdateHeight calls the table view's beginUpdates and endUpdates, so the table view itself will call the -tableView:heightForRowAtIndexPath: delegate method.
- (void)tableViewNeedsToUpdateHeight
{
BOOL animationsEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
[table beginUpdates];
[table endUpdates];
[UIView setAnimationsEnabled:animationsEnabled];
}
In the table view's -tableView:heightForRowAtIndexPath: delegate method we need to calculate the new height for the text view's cell based on the textHeight.
First we need to resize the text view cells height to the maximum available height (after subtracting the height of all other cells in the table view). Then we check if the textHeight is bigger than the calculated height.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat heightForRow = 44.0;
if ([indexPath row] == kRowWithTextViewEmbedded)
{
CGFloat tableViewHeight = [tableView bounds].size.height;
heightForRow = tableViewHeight - ((kYourTableViewsNumberOfRows - 1) * heightForRow);
if (heightForRow < textHeight)
{
heightForRow = textHeight;
}
}
return heightForRow;
}
For a better user experience set the table view's content insets for bottom to e.g. 50.0.
I've tested it on the iPad with iOS 4.2.1 and works as expected.
Florian
I have a custom UITableViewCell with a UIScrollView in it that is wired to the cell controller. When I assign text to the scrollview, some cells get the correct text, some are blank, some redisplay old text and others have the scrollview clipped around the 2nd or 3rd line of text. It seems random on what will happen. I followed the suggestion here of using a timer to fix blank cells, http://www.bdunagan.com/2008/12/08/uitextview-in-a-uitableview-on-the-iphone/, but that didn't help. I placed the timer code in the cellForRowAtIndexPath method.
I've also tried calling
[cell.textview setNeedsDisplay];
after text is assigned to the textview but it doesn't have any affect.
When I use a textfield or label, everything looks fine. However, I need something that can scroll text. Any suggestions on a fix or better way?
Update: Found this on the dev forums (specifically mentions your problem):
https://devforums.apple.com/message/38944#38944
I would follow the link it has some more detailed info.
// view controller
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray* visibleCells = [self.tableView visibleCells];
for (UITableViewCell* cell in visibleCells)
{
if ([cell.reuseIdentifier isEqualToString:kTextViewCellID])
{
[(MTextViewCell*)cell refresh];
}
}
}
// MTextViewCell
- (void)refresh
{
// mucking with the contentOffset causes the textView to redraw itself
CGPoint contentOffset = mTextView.contentOffset;
CGPoint contentOffset1 = { contentOffset.x, contentOffset.y + 1.0f };
mTextView.contentOffset = contentOffset1;
mTextView.contentOffset = contentOffset;
}
Try calling:
[tableView reloadData];
After you update all the textViews.