UITableView correcting scroll position after device rotation

对着背影说爱祢 提交于 2019-12-03 21:01:17

Swift 3 version of Said Ali Samed's answer (willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation are deprecated):

override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
    coordinator.animate(alongsideTransition: { context in
            // Save the visible row position
            self.visibleRows = self.tableView.indexPathsForVisibleRows!
            context.viewController(forKey: UITransitionContextViewControllerKey.from)
        }, completion: { context in
            // Scroll to the saved position prior to screen rotate
            self.tableView.scrollToRow(at: self.visibleRows[0], at: .top, animated: false)
        })
}

Use delegate method willRotateToInterfaceOrientation: to store the visible cell in an array then using the other delegate method of UITableView didRotateFromInterfaceOrientation: scroll to the visible index path that you stored earlier in the array. This is recommended and you don't have to rely on the inconsistent 0.2 seconds wait in a different thread to handle post rotate event.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    // Save the visible row position
    visibleRows = [tableview indexPathsForVisibleRows];
}

-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    // Scroll to the saved position prior to screen rotate
    [tableView scrollToRowAtIndexPath:[visibleRows objectAtIndex:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}

A simple way to do it would be to store the index path of the top visible cell, change the font size then restore the top cell:

NSIndexPath* topCellIndexPath = [[_tableView indexPathsForVisibleRows] objectAtIndex:0];
//Insert code to change font size here
[_tableView scrollToRowAtIndexPath:topCellIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];

This code can be put in any method that's run when orientation changes, such as the orientationChanged: method you put in the question.

This will not take into account having scrolled halfway down a cell so if the height of your cells is large it will not work well and a more complicated method using content offsets would be needed. Let me know if this is the case.

Gio

Since I couldn't get a good answer I'll answer myself. I've looked everywhere but couldn't find a way to do what I wanted so I just used the shouldAutorotateToInterfaceOrientation method to increase the size of font and then start a little thread that sleeps for 0.2 seconds and after that scrolls to the desired row. Thanks for your help.

Edit: Use delegate method willRotateToInterfaceOrientation: to store the visible cell in an array then use the delegate method didRotateFromInterfaceOrientation: to scroll to the visible index path that you recorded in the array.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    // Save the visible row position
    visibleRows = [tableview indexPathsForVisibleRows];
}

-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    // Scroll to the saved position prior to screen rotate
    [tableView scrollToRowAtIndexPath:[visibleRows objectAtIndex:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
prashant
  - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)
fromInterfaceOrientation 
{
NSLog(@"didRotateFromInterfaceOrientation:%d",fromInterfaceOrientation);
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
}

Then I would recommend adding either the interfaceOrientation number or simply the table width to the dequeue cell name that way the tableView knows that cells in one rotation are different from those in another. Like so:

- (UITableViewCell *)tableView:(UITableView *)tv
     cellForRowAtIndexPath:(NSIndexPath *)indexPath
     withType:(NSString *)s_type
{

UITableViewCell *cell = nil;
// add width of table to the name so that rotations will change the cell dequeue names
s_cell = [s_cell stringByAppendingString:
          [NSString stringWithFormat:@"%@%d",@"Width",(int)tv.bounds.size.width]
          ];

NSLog(@"%@",s_cell);
cell = [tv dequeueReusableCellWithIdentifier:s_cell];
if( cell == nil ) { 
    cell = [[[UITableViewCell alloc]
             initWithFrame:CGRectZero reuseIdentifier:s_cell] autorelease];
    }
}
prashant

Firstly, to reload all of your table cells use [self.tableView reloadData]

Secondly, add the line of code that is responsible for the shrinking inside the (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method.

Example:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Some identifier and recycling stuff

if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
    //Make labels smaller
}
else {
    //Make them bigger
}
}

Or you can just call your updateCellForRotate:forRow: method when making them. But I'm not sure how that function works, so I can't be too specific.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!