UIScrollView неправильное смещение с автоматическим макетом

У меня довольно простая конфигурация:

A UIViewController, с дочерним элементом UIScrollView и a UIImageView в этом UIScrollView. Я установил UIImageView с высотой, достаточной для выхода из видимой области (т.е. Выше до 1024pt), и установите ограничение Bottom space to superview моего UIImageView на фиксированное положительное значение (20 например).

project layout

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

Это не просто объяснить, я попытаюсь это сделать: visual representation of previous paragraph

Если вы хотите проверить/просмотреть источник (или раскадровку, я не редактировал ни одной строки кода). Я поместил небольшую демонстрацию в свой github: https://github.com/guillaume-algis/iOSAutoLayoutScrollView

Я прочитал iOS 6 changelog и объяснение по этой теме и думаю, что это правильная реализация второго варианта ( чистый автоматический макет), но в этом случае почему UIScrollView ведет себя так неустойчиво? Я что-то упускаю?

EDIT. Это та же проблема, что и # 12580434 uiscrollview-autolayout-issue. Ответы - обходные пути, так как каждый нашел правильный способ исправить это или это ошибка iOS?

EDIT 2: я нашел другое обходное решение, которое сохраняет положение прокрутки в том же состоянии, в котором пользователь оставил его (это улучшение по сравнению с 12580434 принятый ответ):

@interface GAViewController ()

@property CGPoint tempContentOffset;

@end


@implementation GAViewController

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.tempContentOffset = self.mainScrollView.contentOffset;
    self.scrollView.contentOffset = CGPointZero;
}

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    self.scrollView.contentOffset = self.tempContentOffset;
}

В основном сохраняйте смещение в viewWillAppear, reset до начала координат, а затем восстановите значение в viewDidAppear. Кажется, что проблема возникает между этими двумя вызовами, но я не могу найти ее источник.

Ответ 1

Да, что-то странное произошло с UIScrollView в чистой среде автозапуска. Перечитав примечания к выпуску iOS SDK 6.0 в двадцатый раз, я обнаружил, что:

Обратите внимание, что вы можете заставить subview вид прокрутки отображаться как float (не прокручивать) по другому прокручиваемому контенту, создавая ограничения между представлением и представлением вне поддерева прокрутки, например, просмотр прокрутки.

Решение

Подключить подзону к внешнему виду. Другими словами, к представлению, в которое встроено scrollview.

Поскольку IB не позволяет нам устанавливать ограничения между imageView и представлением вне поддеревьев прокрутки, например, просматривать прокрутки, затем я сделал это в коде.

- (void)viewDidLoad {
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    [self.view removeConstraints:[self.view constraints]];
    [self.scrollView removeConstraints:[self.scrollView constraints]];
    [self.imageView removeConstraints:[self.imageView constraints]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_imageView(700)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageView(1500)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
}

И вау! Он работает!

Ответ 2

Редактирование не работало для меня. Но это сработало:

-(void)viewWillDisappear:(BOOL)animated
{
     [super viewWillDisappear:animated];

     self.tempContentOffset = self.scrollView.contentOffset;
     self.scrollView.contentOffset = CGPointZero;
}

- (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];
     self.scrollView.contentOffset = self.tempContentOffset;
}

Ответ 3

Для меня я пошел в IB, щелкнув мой контроллер просмотра, содержащий свиток. Затем я пошел в Attribute Inspector → View Controller → Extend Edges → Снимите флажок "Под верхними барами" и "Под нижними барами".

Ответ 4

найдено простое решение, просто поместите

[self setAutomaticallyAdjustsScrollViewInsets:NO];

в режиме ViewControllers viewDidLoad

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.

    [self setAutomaticallyAdjustsScrollViewInsets:NO];
}

Ответ 5

У меня была аналогичная проблема с использованием UIScrollView в UIViewController с верхним дополнительным пространством, которое не было видно в Storyboard. Решение для меня состояло в том, чтобы снять флажок "Настроить прокрутки" в свойствах раскадровки ViewController: см. Ответ Дополнительное пространство (вставка) в списке прокрутки во время выполнения

Ответ 6

Добавьте глобальное свойство contentOffset и сохраните текущее содержимоеОфис в viewDidDisappear. После того, как вы вернете метод viewDidLayoutSubviews, вы будете вызывать, и вы можете установить исходное contentOffset.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [self.scrollView setContentOffset:self.contentOffset animated:FALSE];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    self.contentOffset = self.scrollView.contentOffset;
    [self.scrollView setContentOffset:CGPointMake(0, 0) animated:FALSE];
}

Ответ 7

Похоже, что проблема была решена с помощью dispatch_async во время viewWillAppear:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    CGPoint originalContentOffset = self.scrollView.contentOffset;
    self.scrollView.contentOffset = CGPointZero;

    dispatch_async(dispatch_get_main_queue(), ^{
        self.scrollView.contentOffset = originalContentOffset;
    });
}