简体   繁体   English

设备旋转后UITableView校正滚动位置

[英]UITableView correcting scroll position after device rotation

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 of text. 我正在使用UITableView来显示文本块。

Problem is that, increasing the font size increases height of rows in my table view and if I was reading paragraph 320 in portrait mode I get 280 or something similar in landscape mode. 问题是,增加字体大小会增加表视图中行的高度,如果我在纵向模式下读取段落320,我在横向模式下会得到280或类似的东西。

I have set up a rotation notification listener using this code: 我已使用以下代码设置了旋转通知侦听器:

    UIDevice *device = [UIDevice currentDevice];                
    [device beginGeneratingDeviceOrientationNotifications];
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self                                
           selector:@selector(orientationChanged:)
               name:UIDeviceOrientationDidChangeNotification
             object:device];

and tried to save the last paragraph index before rotation and then scroll down to it after the rotation but I can't seem to achieve desired effect. 并尝试在旋转之前保存最后一个段落索引,然后在旋转后向下滚动到它,但我似乎无法达到预期的效果。

What's the best way to handle this kind of situation and where do I actually implement "before" and "after" states of rotation? 什么是处理这种情况的最佳方法,我在哪里实际实现“之前”和“之后”轮换状态?

I'd like it to work on iOS 4+. 我希望它可以在iOS 4+上运行。

Swift 3 version of Said Ali Samed's answer ( willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation are deprecated): Swift 3版本的Said Ali Samed的答案( willRotateToInterfaceOrientationdidRotateFromInterfaceOrientation已弃用):

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. 使用委托方法willRotateToInterfaceOrientation:将可见单元存储在数组中,然后使用UITableView的另一个委托方法didRotateFromInterfaceOrientation:滚动到先前存储在数组中的可见索引路径。 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. 这是推荐的,您不必依赖于在不同线程中的不一致0.2秒等待来处理后旋转事件。

- (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. 此代码可以放在方向更改时运行的任何方法中,例如您在问题中放置的orientationChanged:方法。

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. 如果是这种情况,请告诉我。

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. 我到处寻找,但找不到办法做我想要的,所以我只是使用了shouldAutorotateToInterfaceOrientation方法来增加字体的大小,然后启动一个休眠0.2秒的小线程,之后滚动到所需的行。 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. 编辑:使用委托方法willRotateToInterfaceOrientation:将可见单元格存储在数组中,然后使用委托方法didRotateFromInterfaceOrientation:滚动到您在数组中记录的可见索引路径。

- (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];
}

Firstly, to reload all of your table cells use [self.tableView reloadData] 首先,要重新加载所有表格单元格,请使用[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. 其次,在( UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法中添加负责收缩的代码行。

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. 或者您可以在制作时调用updateCellForRotate:forRow:方法。 But I'm not sure how that function works, so I can't be too specific. 但我不确定这个功能是如何工作的,所以我不能太具体。

  - (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. 然后我建议将interfaceOrientation数或简单的表宽添加到出队单元名称,这样tableView知道一次旋转中的单元格与另一次旋转中的单元格不同。 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];
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM