奇怪的行为UIView动画在手势识别器的sender.view中分页UIScrollView

时间:2013-07-09 14:43:01

标签: iphone ios uiscrollview uiviewanimation uipinchgesturerecognizer

设定:

我在UIScrollView课程的帮助下设置了水平分页LXPagingViews(轻松添加'偷窥'UIScrollView)。这已设置,因此您可以在UIScrollView中的不同视图之间轻弹。我在每个视图中添加了UIPinchGestureRecognizer,因此当用户捏住视图时,它会显示“缩放”动画,然后进入详细视图。屏幕截图更清晰:

My paging UIScrollView

问题:

当我捏合其中一个视图时,缩放动画开始,视图将进入详细视图。这适用于UIScrollView中的最后一个视图除外。在最后一个视图中(无论它有多少视图,只要它有多于2个),当动画播放时,它会在我拍摄的视图后面生成一个新视图,并激活该视图。这使得一个奇怪的动画。此外,视图不会被动画阻止输入,因为它会生成一个新视图。这意味着用户可以通过快速挤压快速生成多个视图。

我希望这个截图有点说明问题:

When the user pinches, a new view spawns behind and animates

我尝试了什么:

  • 调试手势识别器的超级视图子视图,以查看是否有更多视图生成
  • 监控哪些视图获得动画效果。对于用户所做的每一次捏合,另一个视图由于某种原因而变得动画
  • 尝试使用另一种设置图像的方式而不是延迟加载。没帮忙
  • 尝试了其他图片,视图越来越少,方向差异和空白视图。

代码:

将手势识别器添加到视图中:

- (UIView<ReusableView> *)pagingView:(PagingView *)thePagingView reusableViewForPageIndex:(NSUInteger)thePageIndex withFrame:(CGRect)theFrame {
    // View's Identifier
    static NSString *theIdentifier = @"voucherDetailView";
    VoucherDetailView *thePageView = (VoucherDetailView *)[thePagingView dequeueReusableViewWithIdentifier:theIdentifier];
    if (thePageView == nil) {
        thePageView = [[VoucherDetailView alloc] initWithFrame:theFrame];
    } else {
        thePageView.frame = theFrame;
    }

    Voucher *voucher = [_vouchers objectAtIndex:thePageIndex];
    NSURL *fullVoucherImageUrl = [NSURL URLWithString:voucher.fullVoucherImage];
    [thePageView.voucher setImageWithURL:fullVoucherImageUrl placeholderImage:[UIImage imageNamed:@"11283253-nederland_kaart"]];

    // Gesture recognizer    
    UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(openZoomablePinch:)];
    [thePageView addGestureRecognizer:pinchGesture];

    return thePageView;
}

捏合处理程序:

- (void)openZoomablePinch:(UIPinchGestureRecognizer*)sender {
    // Only allow one pinch to activate the animation
    if (sender.state == UIGestureRecognizerStateBegan) {
        // Bring the view to the front so it doesn't clip with the peeping views
        [sender.view.superview bringSubviewToFront:sender.view];
        CGAffineTransform trans = sender.view.transform;
        [UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut
                         animations:^{
                             sender.view.transform = CGAffineTransformScale(sender.view.transform, 1.15, 1.15);
                         }
                         completion:^(BOOL finished) {
                             if (finished && self.view.window) {
                                 sender.view.transform = trans;
                                 [self performSegueWithIdentifier:@"voucherZoomSegue" sender:self];
                             }
                         }];
    }
}

编辑:在收集时在最后一个视图中调用的PagingView代码:

    UIView<ReusableView> *theRightMostReusableView = [self.visibleReusableViews lastObject];
    NSUInteger theRightMostPageIndex = (NSUInteger)floorf(CGRectGetMinX(theRightMostReusableView.frame) / thePageWidth);
    while ((theRightMostPageIndex != MAX(0, self.numberOfItems - 1)) && (theRightMostPageIndex < theToIndex)) {
        theRightMostPageIndex = MIN(theRightMostPageIndex + 1, MAX(0, self.numberOfItems - 1));
        CGFloat theMinX = theRightMostPageIndex * thePageWidth;
        CGRect theRect = CGRectMake(theMinX, 0.0f, thePageWidth, thePageHeight);
        UIView<ReusableView> *theReusableView = [self.dataSource pagingView:self reusableViewForPageIndex:theRightMostPageIndex withFrame:theRect];
        if (!CGRectContainsRect(theRect, theReusableView.frame)) {
            @throw [NSException exceptionWithName:NSInternalInconsistencyException
                                           reason:[NSString stringWithFormat:
                                                   @"theReusableView's frame (%@) must be contained by the given frame (%@)",
                                                   NSStringFromCGRect(theReusableView.frame),
                                                   NSStringFromCGRect(theRect)]
                                         userInfo:nil];
        }
        [self.visibleReusableViews addObject:theReusableView];
        [self addSubview:theReusableView];
    }

编辑2:

我上传了一个小型演示项目here。使用它的方法,在横向设置模拟器(或您的设备),转到Peeping Paging View,并尝试捏合/点击视图(第一个除外)。正如您所看到的,视图会被缩放,除了最后一个视图,它会在其后面生成一个新视图并缩放一个视图('9'保持相同的大小)。

代码位于PeepingPagingViewController.m

解决方案(感谢Oladya Kane):

最右边的可见索引被错误地索引为最左边的可见索引。我必须更改的代码是:

NSUInteger theRightMostPageIndex = (NSUInteger)floorf(CGRectGetMinX(theRightMostReusableView.frame) / thePageWidth);

NSUInteger theRightMostPageIndex = MIN((NSInteger)floorf((CGRectGetMaxX(theRightMostReusableView.frame) - 0.1f) / thePageWidth), MAX(0, self.numberOfItems - 1));

1 个答案:

答案 0 :(得分:1)

我查看了你的演示代码,发现了以下错误:

当您的捏合手势识别器被调用时,您可以为其发送框架设置动画。这会强制PageView布置其子视图 - &gt;它调用你的委托方法

- (UIView<ReusableView> *)pagingView:(PagingView *)thePagingView reusableViewForPageIndex:(NSUInteger)thePageIndex withFrame:(CGRect)theFrame

在这里,您可以向PagingView提供视图,并在最后一个视图下方添加您的视图,从而弄乱您的视图层次结构。我想你可能会在某处使用某种标志来阻止PagingView请求新视图。最奇怪的是,这种行为只发生在最后一个视图中。 希望这会对你有所帮助。

修改

我发现了问题。 在方法

- (void)layoutSubviewsFromIndex:(NSUInteger)theFromIndex toIndex:(NSUInteger)theToIndex

计算最左侧和最右侧可见视图的索引。但最右边的可见索引被错误地计算为最左边的索引,所以,你需要替换这一行:

NSUInteger theRightMostPageIndex = (NSUInteger)floorf(CGRectGetMinX(theRightMostReusableView.frame) / thePageWidth);

有了这个:

NSUInteger theRightMostPageIndex = MIN((NSInteger)floorf((CGRectGetMaxX(theRightMostReusableView.frame) - 0.1f) / thePageWidth), MAX(0, self.numberOfItems - 1));

这将阻止PagingView请求已经可见的最后(最右侧)视图的新视图。