Jak dotąd żadna z odpowiedzi faktycznie działa w pełni. Przypadek kant, że wszystkie one nie są w ten sposób:
- Przejdź do strony 2.
- jednym palcem, opór w kierunku strony 1.
- Połóż drugi palec na ekranie i przeciągnij w kierunku strony 1.
- Podnieś pierwszy palec.
- Powtórz dopóki nie przeciągać przeszłość strona 0.
W takiej sytuacji każde rozwiązanie widziałem tak daleko mija granice stronie 0. Podstawowym problemem jest to, że podstawowa API jest złamane, a rozpoczyna raportowanie przesunięcia treści względem strony 0 bez wywoływania naszego wywołania zwrotnego, aby poinformować nas, że wyświetla inną stronę. W całym tym procesie API wciąż twierdzi, że pokazuje stronę 1, zbliżając się do strony zerowej, nawet jeśli naprawdę znajduje się na stronie zero w kierunku strony -1.
Rozwiązaniem dla tego projektu wada jest wyjątkowo brzydki, ale tutaj jest:
@property (weak,nonatomic) UIPageControl *pageControl;
@property (nonatomic,assign) BOOL shouldBounce;
@property (nonatomic,assign) CGFloat lastPosition;
@property (nonatomic,assign) NSUInteger currentIndex;
@property (nonatomic,assign) NSUInteger nextIndex;
- (void)viewDidLoad {
[super viewDidLoad];
...
self.shouldBounce = NO;
for (id testView in self.pageController.view.subviews) {
UIScrollView *scrollView = (UIScrollView *)testView;
if ([scrollView isKindOfClass:[UIScrollView class]]) {
scrollView.delegate = self;
// scrollView.bounces = self.shouldBounce;
}
}
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController{
return (NSInteger)self.currentIndex;
}
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers{
id controller = [pendingViewControllers firstObject];
self.nextIndex = [viewControllers indexOfObject:controller];
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed{
if(completed) {
// At this point, we can safely query the API to ensure
// that we are fully in sync, just in case.
self.currentIndex = [viewControllers indexOfObject:[pageViewController.viewControllers objectAtIndex:0]];
[self.pageControl setCurrentPage:self.currentIndex];
}
self.nextIndex = self.currentIndex;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
/* The iOS page view controller API is broken. It lies to us and tells us
that the currently presented view hasn't changed, but under the hood, it
starts giving the contentOffset relative to the next view. The only
way to detect this brain damage is to notice that the content offset is
discontinuous, and pretend that the page changed.
*/
if (self.nextIndex > self.currentIndex) {
/* Scrolling forwards */
if (scrollView.contentOffset.x < (self.lastPosition - (.9 * scrollView.bounds.size.width))) {
self.currentIndex = self.nextIndex;
[self.pageControl setCurrentPage:self.currentIndex];
}
} else {
/* Scrolling backwards */
if (scrollView.contentOffset.x > (self.lastPosition + (.9 * scrollView.bounds.size.width))) {
self.currentIndex = self.nextIndex;
[self.pageControl setCurrentPage:self.currentIndex];
}
}
/* Need to calculate max/min offset for *every* page, not just the first and last. */
CGFloat minXOffset = scrollView.bounds.size.width - (self.currentIndex * scrollView.bounds.size.width);
CGFloat maxXOffset = (([viewControllers count] - self.currentIndex) * scrollView.bounds.size.width);
NSLog(@"Page: %ld NextPage: %ld X: %lf MinOffset: %lf MaxOffset: %lf\n", (long)self.currentIndex, (long)self.nextIndex,
(double)scrollView.contentOffset.x,
(double)minXOffset, (double)maxXOffset);
if (!self.shouldBounce) {
CGRect scrollBounds = scrollView.bounds;
if (scrollView.contentOffset.x <= minXOffset) {
scrollView.contentOffset = CGPointMake(minXOffset, 0);
// scrollBounds.origin = CGPointMake(minXOffset, 0);
} else if (scrollView.contentOffset.x >= maxXOffset) {
scrollView.contentOffset = CGPointMake(maxXOffset, 0);
// scrollBounds.origin = CGPointMake(maxXOffset, 0);
}
[scrollView setBounds:scrollBounds];
}
self.lastPosition = scrollView.contentOffset.x;
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
/* Need to calculate max/min offset for *every* page, not just the first and last. */
CGFloat minXOffset = scrollView.bounds.size.width - (self.currentIndex * scrollView.bounds.size.width);
CGFloat maxXOffset = (([viewControllers count] - self.currentIndex) * scrollView.bounds.size.width);
if (!self.shouldBounce) {
if (scrollView.contentOffset.x <= minXOffset) {
*targetContentOffset = CGPointMake(minXOffset, 0);
} else if (scrollView.contentOffset.x >= maxXOffset) {
*targetContentOffset = CGPointMake(maxXOffset, 0);
}
}
}
Zasadniczo, to rejestruje przesunięcie dla każdego zdarzenia przewijania. Jeśli pozycja przewijania przesunęła odległość, która jest niemożliwa (arbitralnie wybrałem 90% szerokości ekranu) w kierunku przeciwnym do kierunku przewijania, kod zakłada, że iOS nas okłamuje i zachowuje się tak, jakby zmiana zakończone poprawnie, traktując przesunięcia jako względne względem nowej strony zamiast starej.
Co klasa to 'view' Twojego UIPageViewController? Jeśli jest to UIScrollView (lub jego podklasa), możesz ustawić właściwość 'bounce' na' NO' w widoku. – Tim
Z tego co rozumiem, UIPageViewController implementuje UIScrollView, ale sama klasa nadal jest UIPageViewController. –
Nie sam kontroler - "widok" kontrolera, który jest właściwością w UIViewController, nadklasie UIPageViewController. – Tim