UITableView корректирует положение прокрутки после поворота устройства

Я строю что-то вроде читалки для книги. Когда пользователь поворачивает телефон, я хочу увеличить размер шрифта. Я использую UITableView для отображения фрагментов текста.

Проблема в том, что увеличение размера шрифта увеличивает высоту строк в моем представлении таблицы, и если я читал абзац 320 в портретном режиме, я получаю 280 или что-то подобное в ландшафтном режиме.

Я настроил прослушиватель уведомлений о вращении, используя этот код:

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

и попытался сохранить индекс последнего абзаца перед поворотом, а затем прокрутить его вниз после поворота, но я не могу добиться желаемого эффекта.

Каков наилучший способ справиться с такой ситуацией и где я действительно реализую состояния вращения «до» и «после»?

Я бы хотел, чтобы он работал на iOS 4+.


person Gio    schedule 04.09.2012    source источник


Ответы (6)


Swift 3 версия ответа Саида Али Самеда (willRotateToInterfaceOrientation и didRotateFromInterfaceOrientation устарели):

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)
        })
}
person Mohit Singh    schedule 29.09.2016
comment
Спасибо за перевод на Swift 3! Работает отлично! - person bgolson; 26.05.2017

Используйте метод делегата willRotateToInterfaceOrientation: для сохранения видимой ячейки в массиве, а затем используйте другой метод делегата UITableView didRotateFromInterfaceOrientation: прокрутите до пути видимого индекса, который вы сохранили ранее в массиве. Это рекомендуется, и вам не нужно полагаться на противоречивое 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];
}
person Said Ali Samed    schedule 22.09.2014

Поскольку я не мог получить хороший ответ, я отвечу сам. Я искал везде, но не смог найти способ сделать то, что хотел, поэтому я просто использовал метод shouldAutorotateToInterfaceOrientation, чтобы увеличить размер шрифта, а затем запустить небольшой поток, который спит в течение 0,2 секунды, а затем прокручивается до нужной строки. Спасибо за вашу помощь.

Редактировать: используйте метод делегата 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];
}
person Gio    schedule 04.09.2012

Простым способом сделать это было бы сохранить индексный путь верхней видимой ячейки, изменить размер шрифта, а затем восстановить верхнюю ячейку:

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

Этот код можно поместить в любой метод, который запускается при изменении ориентации, например, в метод orientationChanged:, который вы указали в вопросе.

Это не будет учитывать прокрутку на полпути вниз по ячейке, поэтому, если высота ваших ячеек велика, это не будет работать, и потребуется более сложный метод, использующий смещения содержимого. Дайте мне знать, если это так.

person Ander    schedule 04.09.2012
comment
Спасибо за помощь. Я просто тестировал этот выход. Я думаю, что это сработает, но не могли бы вы уточнить, где ловить события ориентации willChange и didChange? я должен использовать делегатов? - person Gio; 04.09.2012
comment
Меня больше интересует, когда сохранять topCellIndexPath? - person Gio; 04.09.2012
comment
Вращение устройства не должно изменять положение ячейки, оно меняется только потому, что вы меняете размер шрифта. В любом методе, где вы фиксируете изменение ориентации, вы можете добавить три строки для хранения верхней ячейки, изменить размер шрифта для восстановления верхней ячейки, и все должно работать нормально. (Отредактированный ответ, чтобы отразить это) - person Ander; 04.09.2012
comment
Это именно то, что я сделал, но когда я делал это в методе shouldAutorotateToInterfaceOrientation, он сначала прокручивал, а затем поворачивал экран, поэтому это не имело никакого эффекта. Вот почему я добавил немного сна в потоке, а после этого (в то время как вращение завершено) прокрутите до этого indexPath. Спасибо - person Gio; 04.09.2012

  - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)
fromInterfaceOrientation 
{
NSLog(@"didRotateFromInterfaceOrientation:%d",fromInterfaceOrientation);
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
}

Затем я бы рекомендовал добавить либо номер interfaceOrientation, либо просто ширину таблицы к имени ячейки удаления из очереди, чтобы tableView знал, что ячейки в одном повороте отличаются от ячеек в другом. Вот так:

- (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];
    }
}
person prashant    schedule 04.09.2012

Во-первых, чтобы перезагрузить все ячейки таблицы, используйте [self.tableView reloadData]

Во-вторых, добавьте строку кода, отвечающую за сжатие внутри метода (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath.

Пример:

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

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

Или вы можете просто вызвать свой метод updateCellForRotate:forRow: при их создании. Но я не уверен, как работает эта функция, поэтому не могу быть слишком конкретным.

person prashant    schedule 04.09.2012
comment
Спасибо, но это не решение. Его высота увеличивается, потому что так и должно быть. Я хочу, чтобы он увеличился. Единственное, что мне нужно, это определить, что устройство будет вращаться, но до того, как это произойдет на самом деле. - person Gio; 04.09.2012