ios - 在 UIPageViewController 中禁用弹跳效果

标签 ios iphone uipageviewcontroller bounce

<分区>

我实现了一个包含两个页面的 UIPageViewController。在最右边的页面上,我可以向右滑动,然后将页面拉回来,这样当我松开时,它就会弹回来。当我向左滑动时,左页上也会出现同样的情况。 (弹跳就像您到达 safari 页面底部时发生的情况)

有没有办法禁用弹跳效果?谢谢!

最佳答案

到目前为止,没有一个答案真正有效。他们都失败的边缘情况是这样的:

  1. 滚动到第 2 页。
  2. 用一根手指向第 1 页拖动。
  3. 将第二根手指放在屏幕上并向第 1 页拖动。
  4. 举起第一个手指。
  5. 重复,直到您拖过第 0 页。

在那种情况下,到目前为止我看到的每个解决方案都超出了第 0 页的边界。核心问题是底层 API 被破坏,并且开始报告相对于第 0 页的内容偏移量而不调用我们的回调让我们知道它显示的是不同的页面。在整个过程中,API 仍然声称要显示第 1 页,向第 0 页前进,即使它实际上是在第 0 页向第 -1 页前进。

这个设计缺陷的解决方法非常丑陋,但它是:

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

基本上,它记录了每个滚动事件的偏移量。如果滚动位置在与滚动方向相反的方向上移动了不可能的距离(我任意选择了屏幕宽度的 90%),则代码假定 iOS 在欺骗我们,并且表现得好像过渡正确完成,将偏移量视为相对于新页面而不是旧页面。

关于ios - 在 UIPageViewController 中禁用弹跳效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22795883/

相关文章:

ios - 信号观察者能否访问 ReactiveCocoa 信号的最后发射值?

ios - 字典数组还是带数组的字典?

ios - 如何将其他NSManagedObjects存储为NSManagedObject的属性

iphone - 什么是差异。 b/w @property(非原子,分配)和@property(非原子,保留)

ios - Swift:NSUserDefaults 没有立即设置

ios - pageViewController 上的背景图像反向重叠

ios - 如何使用 Swift 3 在默认 ios APP 中打开文档 URL?

iphone - 如何在iOS中的UIGestureRecognizer中匹配图像形状?

ios - 属性文本 boundingRectWithSize 返回错误的大小

ios - 如何在水平 UIPageViewController 中实现垂直 UIPageViewController