处理子UIScrollView中的触摸事件

Rya*_*ker 6 iphone

我在UIScrollView中显示一系列图像.我非常想复制照片应用程序.

我目前的架构是:

  • 与内容大小父UIScrollView的是足够宽X页+一些额外的空间,在图像之间的利润数字.
  • 每个图像都包含在UIImageView中.
  • 每一个的UIImageView包含在它自己的UIScrollView然后将它们父的UIScrollView的子视图中.

    所以我在父UIScrollView中基本上有一行UIScrollViews.

    父UIScrollView启用了分页,因此我可以在页面之间滚动而不会出现任何问题.

    问题是如何无缝平移放大的图像.我已经覆盖了viewForZoomingInScrollView方法,以返回适当的UIImageView当用户捏输入/输出. 如果缩放比例大于1,我已经覆盖了scrollViewDidEndZooming将父视图的canCancelContentTouches属性设置为的方法NO.

    因此用户可以平移图像.但是,在将触摸事件发送到子视图之前,他们必须将手指放下一会儿才能超过父滚动视图的小延迟.此外,一旦用户在一个图像平移,下/预防和图像不当用户已经达到了当前图像的边界进入可视区域.

    有任何想法吗?

    谢谢.

  • And*_*sov 5

    好极了!我试图用一个UIScrollView解决问题,我想我找到了一个解决方案.

    在用户开始缩放(in viewForZoomingInScrollView:)之前,我将滚动视图切换到缩放模式(删除所有其他页面,重置内容大小和偏移).当用户缩小到1.00(in scrollViewDidEndZooming:withView:atScale:)时,我切换回分页视图(添加所有页面,调整内容大小和偏移).

    这是一个简单的视图控制器的代码,它就是这样做的.此示例在三个大型UIImageView之间切换,缩放和平移.

    请注意,只需要一个具有少量功能的视图控制器,无需子类化UIScrollView等.

    typedef enum {
        ScrollViewModeNotInitialized,           // view has just been loaded
        ScrollViewModePaging,                   // fully zoomed out, swiping enabled
        ScrollViewModeZooming,                  // zoomed in, panning enabled
        ScrollViewModeAnimatingFullZoomOut,     // fully zoomed out, animations not yet finished
        ScrollViewModeInTransition,             // during the call to setPagingMode to ignore scrollViewDidScroll events
    } ScrollViewMode;
    
    @interface ScrollingMadnessViewController : UIViewController <UIScrollViewDelegate> {
        UIScrollView *scrollView;
        NSArray *pageViews;
        NSUInteger currentPage;
        ScrollViewMode scrollViewMode;
    }
    
    @end
    
    @implementation ScrollingMadnessViewController
    
    - (void)setPagingMode {
        NSLog(@"setPagingMode");
        if (scrollViewMode != ScrollViewModeAnimatingFullZoomOut && scrollViewMode != ScrollViewModeNotInitialized)
            return; // setPagingMode is called after a delay, so something might have changed since it was scheduled
        scrollViewMode = ScrollViewModeInTransition; // to ignore scrollViewDidScroll when setting contentOffset
    
        // reposition pages side by side, add them back to the view
        CGSize pageSize = scrollView.frame.size;
    
        NSUInteger page = 0;
        for (UIView *view in pageViews) {
            if (!view.superview)
                [scrollView addSubview:view];
            view.frame = CGRectMake(pageSize.width * page++, 0, pageSize.width, pageSize.height);
        }
    
        scrollView.pagingEnabled = YES;
        scrollView.showsVerticalScrollIndicator = scrollView.showsHorizontalScrollIndicator = NO;
        scrollView.contentSize = CGSizeMake(pageSize.width * [pageViews count], pageSize.height);
        scrollView.contentOffset = CGPointMake(pageSize.width * currentPage, 0);
    
        scrollViewMode = ScrollViewModePaging;
    }
    
    - (void)setZoomingMode {
        NSLog(@"setZoomingMode");
        scrollViewMode = ScrollViewModeInTransition; // to ignore scrollViewDidScroll when setting contentOffset
    
        CGSize pageSize = scrollView.frame.size;
    
        // hide all pages besides the current one
        NSUInteger page = 0;
        for (UIView *view in pageViews)
            if (currentPage != page++)
                [view removeFromSuperview];
    
        // move the current page to (0, 0), as if no other pages ever existed
        [[pageViews objectAtIndex:currentPage] setFrame:CGRectMake(0, 0, pageSize.width, pageSize.height)];
    
        scrollView.pagingEnabled = NO;
        scrollView.showsVerticalScrollIndicator = scrollView.showsHorizontalScrollIndicator = YES;
        scrollView.contentSize = pageSize;
        scrollView.contentOffset = CGPointZero;
    
        scrollViewMode = ScrollViewModeZooming;
    }
    
    - (void)loadView {
        CGRect frame = [UIScreen mainScreen].applicationFrame;
        scrollView = [[UIScrollView alloc] initWithFrame:frame];
        scrollView.delegate = self;
        scrollView.maximumZoomScale = 2.0f;
        scrollView.minimumZoomScale = 1.0f;
    
        UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"red.png"]];
        UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"green.png"]];
        UIImageView *imageView3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"yellow-blue.png"]];
    
        // in a real app, you most likely want to have an array of view controllers, not views;
        // also should be instantiating those views and view controllers lazily
        pageViews = [[NSArray alloc] initWithObjects:imageView1, imageView2, imageView3, nil];
    
        self.view = scrollView;
    }
    
    - (void)setCurrentPage:(NSUInteger)page {
        if (page == currentPage)
            return;
        currentPage = page;
        // in a real app, this would be a good place to instantiate more view controllers -- see SDK examples
    }
    
    - (void)viewDidLoad {
        scrollViewMode = ScrollViewModeNotInitialized;
        [self setPagingMode];
    }
    
    - (void)viewDidUnload {
        [pageViews release]; // need to release all page views here; our array is created in loadView, so just releasing it
        pageViews = nil;
    }
    
    - (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(setPagingMode) object:nil];
        CGPoint offset = scrollView.contentOffset;
        NSLog(@"scrollViewDidScroll: (%f, %f)", offset.x, offset.y);
        if (scrollViewMode == ScrollViewModeAnimatingFullZoomOut && ABS(offset.x) < 1e-5 && ABS(offset.y) < 1e-5)
            // bouncing is still possible (and actually happened for me), so wait a bit more to be sure
            [self performSelector:@selector(setPagingMode) withObject:nil afterDelay:0.1];
        else if (scrollViewMode == ScrollViewModePaging)
            [self setCurrentPage:roundf(scrollView.contentOffset.x / scrollView.frame.size.width)];
    }
    
    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)aScrollView {
        if (scrollViewMode != ScrollViewModeZooming)
            [self setZoomingMode];
        return [pageViews objectAtIndex:currentPage];
    }
    
    - (void)scrollViewDidEndZooming:(UIScrollView *)aScrollView withView:(UIView *)view atScale:(float)scale {
        NSLog(@"scrollViewDidEndZooming: scale = %f", scale);
        if (fabsf(scale - 1.0) < 1e-5) {
            if (scrollView.zoomBouncing)
                NSLog(@"scrollViewDidEndZooming, but zoomBouncing is still true!");
    
            // cannot call setPagingMode now because scrollView will bounce after a call to this method, resetting contentOffset to (0, 0)
            scrollViewMode = ScrollViewModeAnimatingFullZoomOut;
            // however sometimes bouncing will not take place
            [self performSelector:@selector(setPagingMode) withObject:nil afterDelay:0.2];
        }
    }
    
    @end
    
    Run Code Online (Sandbox Code Playgroud)

    可运行的示例项目可从http://github.com/andreyvit/ScrollingMadness/获得(如果您不使用Git,只需单击那里的"下载"按钮).那里有一个自述文件,解释了代码编写原理的原因.

    (该示例项目还说明了如何以编程方式缩放滚动视图,并且具有封装解决方案的ZoomScrollView类.它是一个整洁的类,但不需要此技巧.如果您想要一个不使用ZoomScrollView的示例,在提交历史记录中返回一些提交.)

    PS为了完整起见,有TTScrollView - UIScrollView从头开始重新实现.它是着名的Three20图书馆的一部分.我不喜欢它对用户的感觉,但它确实使得实现分页/滚动/缩放变得简单.

    PPS Apple的真实照片应用程序具有SDK前代码并使用SDK之前的类.人们可以在PhotoLibrary框架内发现从UIScrollView的SDK前版本变体派生的两个类,但是不清楚他们做了什么(并且他们做了很多).我很容易相信这种效果在SDK之前的版本中更难以实现.


    Ben*_*ieb 2

    我必须做一个类似的设置,但我基本上定制了整个事情。我不确定当您到达边缘时,您将如何解决将触摸事件从子 UIScrollView “传递”到父 UISscrollView 的问题。您可以尝试在父 UIScrollView 中覆盖 UITouchesBegan:withEvent: ,然后直接转储到子视图。祝你好运!