Источник UIScrollView изменяется после возврата к UIViewController
у меня есть UIViewController
подкласс как сцена в раскадровке, которая содержит UIScrollView
содержит различные подвиды. Одним из подвидов является UIButton
который переходит в другую сцену UIViewController
подкласс. Когда я вернусь из вида (поп UIViewController
от стека навигационного контроллера), я нахожу, что происхождение вида прокрутки каким-то образом изменилось, хотя contentsize
и contentoffset
кажется правильным.
что также интересно, так это то, что приложение имеет панель вкладок, а когда я вкладываю и возвращаясь к этому виду, вид прокрутки устанавливается правильно со смещением (0, 0).
в этом процессе в основном нет кода, так как это почти все в раскадровке. Поскольку я довольно новичок в использовании раскадровки, я думаю, что делаю что-то неправильно, хотя я не знаю, что. Есть идеи, что это может быть? Возможно, проблемы с калибровкой или ограничения?
16 ответов:
попробуйте это в viewWillAppear контроллера вида вы поп обратно в:
self.scrollView.contentOffset = CGPointMake(0, 0);
Edit: при добавлении кода Питера вы получаете лучшие результаты с помощью:
- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:YES]; self.scrollView.contentOffset = CGPointMake(0, 0); }
плюс
- (void)viewWillDisappear:(BOOL)animated { self.recentContentOffset = self.scrollView.contentOffset; [super viewWillDisappear:animated]; }
и
- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.scrollView.contentOffset = CGPointMake(0, self.recentContentOffset.y); }
вы возвращаетесь в исходное положение прокрутки, не имеете визуального побочного эффекта, и сам вид прокрутки правильно расположен в своем супервизоре (что было проблемой) после возвращения.
на самом деле, я положил эту строку кода в
viewDidDisappear
, и чтобы он помнил смещение, когда вид снова появляется, я добавил эту строку перед нимself.contentOffset = self.scrollView.contentOffset;
а также
- (void)viewDidLayoutSubviews { self.scrollView.contentOffset = self.contentOffset; }
У меня была аналогичная проблема, после увольнения viewController, contentOffset из моего tableView был изменен на (0, -64).
мое решение было немного странным, я попробовал все другие ответы, но не имел успеха, единственное, что исправило мою проблему, - это переключить положение tableView в дереве элементов управления .xib
Это был первый элемент управления в Родительском представлении, как это:
я переместил tableView сразу после ImageView и это сработало:
кажется, что помещение табличного представления в первое положение вызывало проблемы, а перемещение табличного представления в другое положение исправляло проблему.
P. D. я не использую autoLayout ни раскадровки
надеюсь, что это может помочь кому-то!
Я использую collectionView и у меня была аналогичная проблема. Для iOS 11: в инспекторе размеров есть "вставка содержимого". Установите для этого значение "Никогда". Это решило проблему для меня. Надеюсь, это кому-то поможет.
Цель C:
if (@available(iOS 11, *)) { [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; }
Swift:
if #available(iOS 11, *) { UIScrollView.appearance().contentInsetAdjustmentBehavior = .never }
в iOS 11 я столкнулся с аналогичной проблемой, когда, когда я возвращаюсь после появления контроллера вида, tableview в предыдущем viewcontroller использовал для автоматической настройки его вставки содержимого, что привело к резкому скачку содержимого tableview сверху. Следующее решение работает для меня в iOS 11.
в раскадровке выберите tableview и перейдите в Инспектор атрибутов и снимите флажок "Автоматически" для полей высоты строки и оценки. Также измените содержимое вставки из "автоматического" на "никогда".
[]
к сожалению, предложения Питера и Макмарка не сработали для меня (Xcode 5 w/ auto-layout). Решение состояло в том, чтобы перейти к раскадровке, выбрать контроллер вида и
Reset to Suggested Constraints in View Controller
.
Я недавно отвечал на вопрос о подобной проблеме, но в другом контексте: рамка не отражает ограничения автоматической компоновки после отклонения контроллера модального вида
в моем случае начало пользовательского представления контейнера внутри представления прокрутки смещается, а не начало самого представления прокрутки. Что касается автоматической компоновки, пользовательский вид контейнера прикрепляется к четырем сторонам вида прокрутки.
создается представление контейнера и настроен программно, а не в IB. Хотя ограничения представления контейнера остаются неизменными, начало представления контейнера смещается после отклонения контроллера модального представления. Это разъединение между ограничениями и фреймами необъяснимо.
еще более примечательно то, что проблема смещения начала координат осталась после того, как я удалил и повторно добавил все связанные ограничения.
Я придумал аналогичное решение: сохранить значение текущего содержимого смещение, установите смещение содержимого на "ноль", позвольте системе поменять местами ваши представления, а затем восстановите смещение содержимого (т. е. танец смещения содержимого).
в моем случае, однако, мне пришлось предпринять дополнительные шаги, чтобы решить эту проблему. Мой вид прокрутки-это вид прокрутки подкачки, который получает свои подвиды из метода tilePages (демонстрируется в старом видео WWDC). Изменение смещения содержимого представления прокрутки запускает метод tilePages с помощью метода scrollViewDidScroll: UIScrollViewDelegate. Мне пришлось установить флаг, чтобы отключить tilePages в то время как я сделал content-offset танец, в противном случае приложение будет сбой.
надеюсь, я могу удалить код для content-offset dance при обновлении до iOS 7.
продолжение вопроса при выполнении текущих ответов:
вторая попытка открыть представленный контроллер вида, не выходя из представленного контроллера вида, проблема осталась. Вот почему я отправляю точные шаги это привело к моему решению.
Итак, Я сброс ограничения collectionView в раскадровке, убедившись, что они были прикреплены к основному presentingViewController вид.
добавил:
self.view.translatesAutoresizingMaskIntoConstraints = YES;
внутриviewDidLoad
контроллера представления вида.и хранящиеся, в частном порядке, contentOffset представления коллекции до появления модально представленного контроллера представления:
(void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.contentOffset = self.collectionView.contentOffset; self.collectionView.contentOffset = CGPointZero; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.collectionView.contentOffset = self.contentOffset; }
недавно я столкнулся с этой ошибкой, может быть решена с помощью этих кодов:
// fix ios 6 bug begin - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7) { isRecoredforios6 = YES; recordedOffsetforios6 = self.tableView.contentOffset; recordedSizeforios6 = self.tableView.contentSize; } } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; if (isRecoredforios6) { isRecoredforios6 = NO; self.tableView.contentSize = recordedSizeforios6; self.tableView.contentOffset = recordedOffsetforios6; } } // fix ios 6 bug end
спасибо Питер Джейкобс!
- (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7) { _isRecoredforios6 = YES; _recordedOffsetforios6 = _scrollView.contentOffset; _recordedSizeforios6 = _scrollView.contentSize; _scrollView.contentOffset = CGPointZero; } } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; if (_isRecoredforios6) { _isRecoredforios6 = NO; _scrollView.contentSize = _recordedSizeforios6; _scrollView.contentOffset = _recordedOffsetforios6; } }
я исправил эту ошибку ios6, может быть решена с помощью этих кодов. Я решил, что ошибка произошла в Scrollview. Спасибо прежде всего моим друзьям!
после того, как попробовал много вещей:
- установите automaticallyAdjustsScrollViewInsets в false, чтобы увидеть, если проблема была с панелью навигации.
- игра с фиксированной высоты ячейки...
- кэширование высоты ячеек...
- сохранение свойства со смещением представления таблицы, кэширование его и установка его на viewWillAppear...
- etc.
Я понял, что вопрос был о
estimatedRowHeight
значением, которое было установлено в 50px, когда это должно быть ~150px. Обновление значения исправило проблему, и теперь табличное представление сохраняет то же смещение.
ни один из вышеперечисленных не работал для меня, мне удалось сделать свою собственную анимацию Push/Pull, и она работает как шарм.
во-первых, добавьте этот класс, который реализует сценарий Push
class PushAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.5 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { // get reference to our fromView, toView and the container view that we should perform the transition in let container = transitionContext.containerView let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)! let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! // start the toView to the right of the screen var frame = toView.frame frame.origin.x = container.frame.width toView.frame = frame // add the both views to our view controller container.addSubview(toView) container.addSubview(fromView) // get the duration of the animation let duration = self.transitionDuration(using: transitionContext) // perform the animation! UIView.animate(withDuration: duration, animations: { var frame = fromView.frame frame.origin.x = -container.frame.width fromView.frame = frame toView.frame = container.bounds }, completion: { _ in // tell our transitionContext object that we've finished animating transitionContext.completeTransition(true) }) } }
затем добавьте этот класс, который реализует pop scenario
import Foundation import UIKit class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.5 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { // get reference to our fromView, toView and the container view that we should perform the transition in let container = transitionContext.containerView let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)! let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)! // set up from 2D transforms that we'll use in the animation let offScreenRight = CGAffineTransform(translationX: container.frame.width, y: 0) // start the toView to the right of the screen var frame = toView.frame frame.origin.x = -container.frame.width toView.frame = frame // add the both views to our view controller container.addSubview(toView) container.addSubview(fromView) // get the duration of the animation let duration = self.transitionDuration(using: transitionContext) // perform the animation! UIView.animate(withDuration: duration, animations: { fromView.transform = offScreenRight toView.frame = container.bounds }, completion: { _ in // tell our transitionContext object that we've finished animating transitionContext.completeTransition(true) }) } }
затем добавьте эту строку в метод viewDidLoad, чтобы изменить делегат NavigationController по умолчанию
self.navigationController?.delegate = self
и увидеть волшебство :)
я использовал комбинацию различных решений, размещенных повсюду на SO, и придумал этот подкласс:
// Keeps track of the recent content offset to be able to restore the // scroll position when a modal viewcontroller is dismissed class ScrollViewWithPersistentScrollPosition: UIScrollView { // The recent content offset for restoration. private var recentContentOffset: CGPoint = CGPoint(x: 0, y: 0) override func willMove(toWindow newWindow: UIWindow?) { if newWindow != nil { // save the scroll offset. self.recentContentOffset = self.contentOffset } super.willMove(toWindow: newWindow) } override func didMoveToWindow() { if self.window != nil { // restore the offset. DispatchQueue.main.async(execute: { // restore it self.contentOffset = self.recentContentOffset }) } super.didMoveToWindow() } }
протестировано на iOS 11.2 / Xcode версии 9.2.