UIPageViewController перемещается на неправильную страницу со стилем перехода по прокрутке

Мой UIPageViewController отлично работал в iOS 5. Но когда появился iOS 6, я хотел использовать новый стиль перехода с прокруткой (UIPageViewControllerTransitionStyleScroll) вместо стиля завивки страницы. Это вызвало разрыв моего UIPageViewController.

Он отлично работает, только после того, как я вызвал setViewControllers:direction:animated:completion: После этого в следующий раз, когда пользователь прокручивается вручную на одну страницу, мы получаем неправильную страницу. Что здесь не так?

    Моим обходным путем этой ошибки было создание блока, когда он был завершен, который устанавливал один и тот же диспетчер представлений, но без анимации

     __weak YourSelfClass *blocksafeSelf = self; [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:^(BOOL finished){ if(finished) { dispatch_async(dispatch_get_main_queue(), ^{ [blocksafeSelf.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];// bug fix for uipageview controller }); } }]; 

    На самом деле это ошибка в UIPageViewController. Это происходит только со стилем прокрутки (UIPageViewControllerTransitionStyleScroll) и только после вызова setViewControllers:direction:animated:completion: with animated: YES . Таким образом, существует два способа обхода:

    1. Не используйте UIPageViewControllerTransitionStyleScroll.

    2. Или, если вы вызываете setViewControllers:direction:animated:completion: используйте только animated:NO .

    Чтобы отчетливо увидеть ошибку, вызовите setViewControllers:direction:animated:completion: а затем в интерфейсе (как пользователь) перемещайтесь влево (назад) на предыдущую страницу вручную. Вы вернетесь на неверную страницу: не на предыдущую страницу, а на страницу, на которой вы были, когда setViewControllers:direction:animated:completion:

    Причиной ошибки является то, что при использовании стиля прокрутки UIPageViewController выполняет какое-то внутреннее кэширование. Таким образом, после вызова setViewControllers:direction:animated:completion: он не может очистить свой внутренний кеш. Он думает, что знает, что представляет собой предыдущая страница. Таким образом, когда пользователь перемещается влево на предыдущую страницу, UIPageViewController не может вызвать метод pageViewController:viewControllerBeforeViewController: или вызывает его с помощью неправильного controllerа текущего вида.

    Я опубликовал фильм, который наглядно демонстрирует, как увидеть ошибку:

    http://www.apeth.com/PageViewControllerBug.mov

    EDIT Эта ошибка, вероятно, будет исправлена в iOS 8.

    EDIT Еще один интересный обходной путь для этой ошибки см. В этом ответе: https://stackoverflow.com/a/21624169/341994

    Вот «грубая» сущность, которую я собрал. Он содержит альтернативу UIPageViewController, которая страдает от болезни Альцгеймера (т. Е. Она не имеет внутреннего кэширования реализации Apple).

    Этот class не является полным, но он работает в моей ситуации (а именно: горизонтальная прокрутка).

    Эта ошибка все еще существует в iOS9. Я использую то же обходное решение, что и Джордж Цифрикас, но версия Swift:

      pageViewController.setViewControllers([page], direction: direction, animated: true) { done in if done { dispatch_async(dispatch_get_main_queue()) { self.pageViewController.setViewControllers([page], direction: direction, animated: false, completion: {done in }) } } } 

    ЗАЯВЛЕНИЕ:

    Похоже, что Apple заметила, что разработчики используют UIPageViewController в самых разных приложениях, которые выходят за frameworks первоначально предназначенных Apple, основанных на их дизайне. Вместо того, чтобы использовать его в жесткой линейной моде, ПВХ часто используется для программного перехода к случайным позициям в структурированной среде. Таким образом, они улучшили реализацию UIPageViewController, и теперь class вызывает оба callback-процесса DataSource

     - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController 

    после установки нового ContentViewController на UIPageViewController с

     [self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil]; 

    даже если анимированный поворот страниц скорее предполагает линейный прогресс в иерархии страниц, например, книги или PDF с последовательными страницами. Хотя, я сомневаюсь, что Apple с точки зрения HIG очень любит видеть, что ПВХ используется таким образом, но – он не нарушает совместимость в обратном направлении, это было легко исправить, так что – они в конце концов это сделали. На самом деле это всего лишь один вызов одного из двух методов DataSource, который абсолютно не нужен в линейной среде, где страницы (ViewControllers) уже были обналичены для последующего использования.

    Однако, даже если это усовершенствование может оказаться очень удобным для определенных случаев использования, первоначальное поведение classа НЕ считается ошибкой. Тот факт, что многие разработчики делают – также в других сообщениях о SO, которые обвиняют UIPageViewController в неправильном поведении, – скорее подчеркивает широко распространенное заблуждение в отношении его дизайна, назначения и функциональности.

    Не пытаясь оскорбить кого-либо из моих коллег-разработчиков здесь, на этом великом объекте, я тем не менее решил не удалять свое первоначальное «разбирательство», которое ясно объясняет OP механикой ПВХ и почему его предположение неверно, что он должен иметь дело с ошибкой здесь ,

    Это может также быть полезно для любого другого разработчика-разработчика, который борется с некоторыми сложностями в реализации UIPageViewController!


    ОРИГИНАЛЬНЫЙ ОТВЕТ:

    Прочитав все ответы снова и снова – включив принятый, остается еще одна вещь, чтобы сказать …

    Дизайн UIPageViewController абсолютно БЕЗОПАСНЫЙ, и все хаки, которые вы отправляете, чтобы обойти предполагаемую ошибку, – это не что иное, как средства для ваших собственных ошибочных предположений, потому что вы справились с этим в первую очередь !!!

    БЕЗ ОСТРОВА! Вы просто сражаетесь с каркасом. Я объясню, почему!


    Существует столько разговоров о номерах страниц и индексах! Это понятия, о которых controller ничего не знает! Единственное, что он знает, – это показ некоторого контента (кстати, предоставленного вами как dataViewController ), и что он может сделать что-то вроде анимации справа / слева, чтобы подражать повороту страницы. CURL или SCROLL … !!!

    В мире pageViewController существует только текущее SPACE (давайте назовем его именно таким образом, чтобы избежать путаницы со страницами и индексами).

    Когда вы вначале устанавливаете pageViewController, он только умудряется об этом самом SPACE . Только когда вы начинаете панорамирование своего вида, он начинает спрашивать свой DataSource что он в конечном итоге должен отображать, если произойдет щелчок влево / вправо. Когда вы начинаете панорамирование влево, ПВХ сначала запрашивает BEFORE-SPACE а затем AFTER-SPACE , в случае, если вы начинаете вправо, он делает это наоборот.

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

    Старое пространство BEFORE (от анимации) в случае завершенного поворота вправо полностью устарело и освобождается как можно скорее. Старый center теперь новый BEFORE а бывший AFTER – новый center . Все просто сдвинулось на один шаг вправо.

    Итак, не говорите о том, « какая страница » или « какой бы ни был индекс » – просто – есть ли место BEFORE или AFTER . Если вы вернете NIL в один из обратных вызовов DataSource ПВХ просто предполагает, что он находится на одном range of SPACES вашего range of SPACES . Если вы вернете NIL для обоих обратных вызовов, он предполагает, что он показывает только one and only SPACE и никогда больше не будет снова вызывать обратный вызов DataSource ! Логика зависит от вас! Вы определяете страницы и индексы в своем коде! Не ПВХ !!!


    Для пользователя classа есть два способа взаимодействия с ПВХ.

    • A pan-gesture that indicates whether a turn to the BEFORE/AFTER space is desired
    • A method - namely setViewControllers:direction:animated:completion:

    Этот метод делает то же самое, что и жест панорамы. Вы указываете направление (например, UIPageViewControllerNavigationDirectionBackward/Forward ) для анимации – если есть одно предназначение, которое, другими словами, означает только ->, идущее до или после …

    Опять же – не упоминание индексов, номеров страниц и т. Д …. !!!

    Это просто программный способ добиться того же жеста! И ПВХ делает все правильно, показывая старый контент снова, двигаясь назад влево после того, как в первую очередь переместился вправо. Помните – он просто показывает контент (который вы предоставляете) структурированным образом – это 'single page turn' по дизайну !!!

    Это концепция поворота страницы – или книга, если вам нравится этот термин лучше!

    Просто потому, что вы делаете это, отправляя СТРАНИЦУ 8 после СТРАНИЦЫ 1, это не означает, что ПВХ вообще заботится о вашем скрученном мнении о том, как книга должна работать. И пользователей ваших приложений нет. Перемещение вправо и назад влево обязательно должно привести к достижению исходной страницы – если сделано с анимацией. И вы должны исправить ошибку, найдя решение для этой катастрофы. Не обвиняйте его в UIPageViewController . Он отлично работает!

    Просто спросите себя: вы бы сделали то же самое с анимацией PAGE-CURL ? НЕТ? Ну, и вы не должны с анимацией SCROLL !!! Анимированный поворот страницы – это поворот страницы и только поворот страницы! В любом режиме! И если вы решите оторвать СТРАНИЦУ 2 к СТРАНИЦЕ 7 вашей КНИГИ, это прекрасно! Но просто не ожидайте, что UIPageViewController придумает несуществующую страницу PAGE 7, когда вернется к последней странице, если вы не скажете, что все изменилось …


    Если вы действительно хотите достичь нескоординированного перехода в другое место, сделайте это без анимации! В большинстве случаев это будет не очень элегантно, но … возможно … –

    И ПВХ даже хорошо играет! При переходе на новый SPACE без анимации он будет просить вас дальше по дороге для обоих – controllerа BEFORE и AFTER . Таким образом, ваша прикладная логика может идти в ногу с ПВХ …

    Но с анимацией, которую вы всегда передаете, переходите в предыдущее / следующее пространство ( BEFORE - AFTER ). Так что логично, что нет необходимости вообще в ПВХ, чтобы снова спросить о пространстве, которое он уже знает, когда оживляет очередь страниц !!!

    Если вы хотите увидеть СТРАНИЦУ 7, когда возвращаетесь влево после того, как анимировали с PAGE 1 вправо – ну, я бы сказал – это определенно ваша собственная проблема!


    И на всякий случай, если вы ищете лучшее решение, чем взломать «завершение блока» из принятого ответа (потому что с ним вы заранее делаете работу, чтобы что-то, возможно, даже не использовалось дальше по дороге), используйте распознаватель жестов делегат:

     - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 

    Установите DataViewController вашего PVC здесь (без анимации), если вы действительно собираетесь вернуться налево к СТРАНИЦЕ 7, и DataSource будет запрашиваться до и после, и вы можете отправить любую страницу, которая вам нравится! С флагом или иваром, которые вы должны были спрятать при выполнении вашего неконтролируемого перехода с СТРАНИЦ с 1 по 8, это не должно быть проблемой …


    И когда люди продолжают жаловаться на ошибку в ПВХ – делают 2 оборота страницы, когда предполагается сделать только один поворот – укажите их в этой статье.

    Такая же проблема – запуск неанимированных setViewControllers: метод в жесте перехода вызовет точно такой же хаос. Вы считаете, что вы устанавливаете новый центр – DataSource запрашивается для нового контролера данных BEFORE - AFTER – вы сбросите счетчик индексов … – Ну, это кажется ОК …

    Но – после всего этого бизнеса ПВХ заканчивает свой переход / анимацию и хочет знать о следующем (еще неизвестном ему) dataViewController ( BEFORE или AFTER ), а также запускает DataSource . Это абсолютно оправдано! Он должен знать, где находится в своем маленьком мире BEFORE - CENTER - AFTER , и быть готовым к следующему повороту.

    Но ваша программная логика добавляет еще один индекс ++ к его логике и внезапно попадает на 2 страницы! И это одно от того, где вы думаете.

    И вы должны это учитывать! Не UIPageViewController !!!


    Это точно точка DataSourceProtocol, имеющая только два метода! Он хочет быть как можно более общим, оставляя вам пространство и свободу, чтобы определить свою собственную логику и не зацикливаться на чужих особых идеях и прецедентах! Логика полностью зависит от вас. И только потому, что вы найдете такие функции, как

     - (DataViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard position:(GSPositionOfDataViewController)position; - (NSUInteger)indexOfViewController:(DataViewController *)viewController; 

    во всех экземплярах с копией / вставкой в ​​облаке не обязательно означает, что вы должны есть эту пищу заранее приготовленной пищи! Расширьте их так, как вам нравится! Просто посмотрите выше – в моей подписи вы найдете аргумент 'position:' ! Я продлил это, чтобы узнать позже, если завершенный поворот страницы был правильным или левым поворотом. Потому что делегат, к сожалению, просто говорит вам, завершена ли ваша очередь или нет! Это не говорит вам о направлении! Но это иногда имеет значение для подсчета индексов, в зависимости от потребности вашего приложения …

    Сходите с ума – они ваши …

    СЧАСТЛИВЫЙ КОДИРОВАНИЕ !!!

    Interesting Posts

    Как я могу отключить компьютер через определенное время?

    Что такое байт-коды и как работает JVM

    Как объединить значения из нескольких строк в одну строку? Имейте модуль, но вам нужны переменные, объясняющие

    Создание селектора из имени метода с параметрами

    addID в jQuery?

    У IE автономный браузер все еще существует (и работает на Win 7?)

    C #: Разница (ковариация / контравариантность) другое слово для polymorphismа?

    Как я могу сделать Windows Paint сохранить по умолчанию в формате JPEG?

    Границы gradleиента CSS3

    Изменение даты файла изображения

    Почему мой подclass не может получить доступ к защищенной переменной своего суперclassа, когда он находится в другом пакете?

    Tmux вызывает проблемы с Bash up-arrow

    Загруженное изображение доступно только после обновления страницы

    Почему «dd» не работает для создания загрузочного USB?

    Захват экрана на сеансе рабочего стола сервера

    Давайте будем гением компьютера.