我在UIScrollView中显示一系列图像.我非常想复制照片应用程序.
我目前的架构是:
与内容大小父UIScrollView的是足够宽X页+一些额外的空间,在图像之间的利润数字.
每个图像都包含在UIImageView中.
每一个的UIImageView包含在它自己的UIScrollView然后将它们父的UIScrollView的子视图中.
所以我在父UIScrollView中基本上有一行UIScrollViews.
父UIScrollView启用了分页,因此我可以在页面之间滚动而不会出现任何问题.
问题是如何无缝平移放大的图像.我已经覆盖了viewForZoomingInScrollView
方法,以返回适当的UIImageView当用户捏输入/输出. 如果缩放比例大于1,我已经覆盖了scrollViewDidEndZooming
将父视图的canCancelContentTouches
属性设置为的方法NO
.
因此用户可以平移图像.但是,在将触摸事件发送到子视图之前,他们必须将手指放下一会儿才能超过父滚动视图的小延迟.此外,一旦用户在一个图像平移,下/预防和图像不当用户已经达到了当前图像的边界进入可视区域.
有任何想法吗?
谢谢.
Andrey Taran.. 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{ 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
可运行的示例项目可从http://github.com/andreyvit/ScrollingMadness/获得(如果您不使用Git,只需单击那里的"下载"按钮).那里有一个自述文件,解释了代码编写原理的原因.
(该示例项目还说明了如何以编程方式缩放滚动视图,并且具有封装解决方案的ZoomScrollView类.它是一个整洁的类,但不需要此技巧.如果您想要一个不使用ZoomScrollView的示例,在提交历史记录中返回一些提交.)
PS为了完整起见,有TTScrollView - UIScrollView从头开始重新实现.它是着名的Three20图书馆的一部分.我不喜欢它对用户的感觉,但它确实使得实现分页/滚动/缩放变得简单.
PPS Apple的真实照片应用程序具有SDK前代码并使用SDK之前的类.人们可以在PhotoLibrary框架内发现从UIScrollView的SDK前版本变体派生的两个类,但是不清楚他们做了什么(并且他们做了很多).我很容易相信这种效果在SDK之前的版本中更难以实现.
好极了!我试图用一个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{ 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
可运行的示例项目可从http://github.com/andreyvit/ScrollingMadness/获得(如果您不使用Git,只需单击那里的"下载"按钮).那里有一个自述文件,解释了代码编写原理的原因.
(该示例项目还说明了如何以编程方式缩放滚动视图,并且具有封装解决方案的ZoomScrollView类.它是一个整洁的类,但不需要此技巧.如果您想要一个不使用ZoomScrollView的示例,在提交历史记录中返回一些提交.)
PS为了完整起见,有TTScrollView - UIScrollView从头开始重新实现.它是着名的Three20图书馆的一部分.我不喜欢它对用户的感觉,但它确实使得实现分页/滚动/缩放变得简单.
PPS Apple的真实照片应用程序具有SDK前代码并使用SDK之前的类.人们可以在PhotoLibrary框架内发现从UIScrollView的SDK前版本变体派生的两个类,但是不清楚他们做了什么(并且他们做了很多).我很容易相信这种效果在SDK之前的版本中更难以实现.