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;
});
}