UITableView correcting scroll position after device rotation

后端 未结 6 702
予麋鹿
予麋鹿 2021-01-03 13:45

I\'m building something like a reader for a book. When the user rotates the phone I want to increase the font size. I\'m using a UITableView to display chunks o

相关标签:
6条回答
  • 2021-01-03 13:52

    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];
    }
    
    0 讨论(0)
  • 2021-01-03 13:53

    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.

    0 讨论(0)
  • 2021-01-03 13:56

    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.

    0 讨论(0)
  • 2021-01-03 13:57

    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)
            })
    }
    
    0 讨论(0)
  • 2021-01-03 13:58

    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];
    }
    
    0 讨论(0)
  • 2021-01-03 14:06
      - (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];
        }
    }
    
    0 讨论(0)
提交回复
热议问题