UIScrollView缩放不适用于Autolay

使用严格的自动布局环境缩放UIScrollView似乎不起作用。

这是特别令人沮丧的,因为iOS 6的发行注记当然让我相信它应该写在这里的“纯自动布局方法”这里http://developer.apple.com/library/ios/#releasenotes/General/RN- iOSSDK-6_0 / _index.html

我看了第202,228和232期的WWDC 2012幻灯片,并没有看到这个答案。

我在网上看到的唯一一个专门针对这个问题的问题是UIScrollView缩放与自动布局 ,但它不提供代码的问题,没有答案。

这个用户https://stackoverflow.com/users/341994/matt已经给了UIScrollView的自动布局问题很多很好的回答,甚至连接到git hub上的代码,但是我一直没能find任何答案。

我试图把这个问题归结为绝对的最低限度来说明问题。

我创build了一个新的单视图应用程序与故事板,并没有在界面生成器的变化。

我添加了一个大的图片文件到项目“pic.jpg”。

SVFViewController.h

#import <UIKit/UIKit.h> @interface SVFViewController : UIViewController <UIScrollViewDelegate> @property (nonatomic) UIImageView *imageViewPointer; @end 

SVFViewController.m

 #import "SVFViewController.h" @interface SVFViewController () @end @implementation SVFViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIScrollView *scrollView = [[UIScrollView alloc] init]; UIImageView *imageView = [[UIImageView alloc] init]; [imageView setImage:[UIImage imageNamed:@"pic.jpg"]]; [self.view addSubview:scrollView]; [scrollView addSubview:imageView]; scrollView.translatesAutoresizingMaskIntoConstraints = NO; imageView.translatesAutoresizingMaskIntoConstraints = NO; self.imageViewPointer = imageView; scrollView.maximumZoomScale = 2; scrollView.minimumZoomScale = .5; scrollView.delegate = self; NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView,imageView); NSLog(@"Current views dictionary: %@", viewsDictionary); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]]; [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView]|" options:0 metrics: 0 views:viewsDictionary]]; } -(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView{ return self.imageViewPointer; } @end 

注意我做了一个特别的努力,就像在iOS 6发行说明中提供的示例代码一样,只是尽量减less实现缩放。

那么,这个问题呢?

当你运行这个应用程序并在滚动视图中平移时,一切都很好。 但是,当你放大这个问题是显而易见的,图像来回闪烁,滚动视图内的图像放置更加错误的每个缩放。

看起来好像有争夺imageView的内容偏移的战斗,似乎它被设置为不同的值由两个不同的东西,每个“缩放”。 (imageView的内容偏移属性的NSLog似乎证实了这一点)。

我在这里做错了什么? 有谁知道如何在纯粹的自动布局环境中实现在UIScrollView中实现缩放function。 这里有没有一个这样的例子?

请帮忙。

再次阅读iOS SDK 6.0发行说明,我发现:

请注意,您可以通过在视图和滚动视图的子树之外的视图(如滚动视图的超级视图)之间创build约束来使滚动视图的子视图浮动(不滚动)其他滚动内容。

将你的子视图连接到外部视图。 换句话说,就是embeddedscrollview的视图。

并以下面的方式应用约束我已经得到它的工作:

 [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView(width)]" options:0 metrics:@{@"width":@(self.imageViewPointer.image.size.width)} views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView(height)]" options:0 metrics:@{@"height":@(self.imageViewPointer.image.size.height)} views:viewsDictionary]]; 

发生的问题是在缩放过程中imageview位置的变化。 在缩放过程中,imageview的原点位置将变为负值。 我相信这就是为什么生涩的运动发生。 同样,缩放完成后,imageView仍然位于错误的位置,这意味着滚动看起来将被抵消。

如果你实现-(void) scrollViewDidZoom:(UIScrollView *)scrollView并loggingUIImageView在这段时间的帧,你可以看到它的原点改变。

我最终通过实施这样的策略来解决问题

此外,在缩放时更改contentView的框架

 -(void) scrollViewDidZoom:(UIScrollView *)scrollView { CGRect cFrame = self.contentView.frame; cFrame.origin = CGPointZero; self.contentView.frame = cFrame; } 

这些解决scheme都有点工作。 这是我做的,没有黑客或子类需要,这个设置:

 [view] [scrollView] [container] [imageView] [imageView2] 
  1. 在IB中,勾选scrollView顶部,顶部,底部和尾部来view
  2. container顶部,顶部,底部和尾部连接到scrollView
  3. container的center-x和center-y连接到scrollView center-x和center-y, 在构build时标记为remove 。 这只是为了使IB中的警告消失。
  4. 实现viewForZoomingInScrollView:在视图控制器,应该是scrollView的委托,并从该方法返回container
  5. 当设置imageView的图像时,确定最小缩放比例(现在它将以原始大小显示)并将其设置为:

     CGSize mySize = self.view.bounds.size; CGSize imgSize = _imageView.image.size; CGFloat scale = fminf(mySize.width / imgSize.width, mySize.height / imgSize.height); _scrollView.minimumZoomScale = scale; _scrollView.zoomScale = scale; _scrollView.maximumZoomScale = 4 * scale; 

这适用于我,设置图像放大滚动视图显示整个图像,并允许放大到初始大小的4倍。

假设你在“ UIView ”里面的“ UIScrollView ”里有故事板“ UIImageView ”。

将“ UIScrollView ”中的所有约束与视图控制器+ UIView中的两个约束( Horizontal Space - Scroll View - ViewHorizontal Space - Scroll View - View )链接起来。

为“ UIScrollView ”设置视图控制器AS委托。

然后实现这个代码:

 @interface VC () <UIScrollViewDelegate> @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray* constraints; @end @implementation FamilyTreeImageVC - (void)viewDidLoad { [super viewDidLoad]; [self.scrollView removeConstraints:self.constraints]; } - (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.imageView; } -(void) scrollViewDidZoom:(UIScrollView *)scrollView { CGRect cFrame = self.imageView.frame; cFrame.origin = CGPointZero; self.imageView.frame = cFrame; } 

尝试从仅使用scrollView的故事板项目实现缩放时,遇到同样的问题。

我通过添加一个单独的捏手势识别器来修复它。 我只是把它从工具箱拖到我的场景中。 然后我把它连接到一个叫做“doPinch”的动作来实现缩放。 我把它连接到我称之为“pinchRecognizer”的sockets,以便我可以访问它的缩放属性。 这似乎覆盖了scrollView的内置缩放,跳跃消失。 也许它不会在起源上犯同样的错误,或者更优雅地处理。 在IB的布局之上是非常小的工作。

只要将捏合手势识别器引入到场景中,就需要动作和viewForZoomingInScrollView方法。 错过任何一个,缩放停止工作。

我的视图控制器中的代码是这样的:

 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.zoomableImage; } - (IBAction)doPinch:(id)sender { NSLog(@"In the pinch action now with scale: %f", self.pinchRecognizer.scale); [scrollView setZoomScale:self.pinchRecognizer.scale animated:NO]; } 

这个非常基本的实现有一个副作用:当你回到一个放大的图像,放大一些更大的比例值是1.0f,所以它跳回到原来的规模。

您可以通过引入属性“currentScale”来进行sorting,以便跟踪缩放比例,并在再次开始缩放时设置缩放手势识别器缩放比例。 您需要使用手势识别器的状态属性:

 - (IBAction)doPinch:(id)sender { NSLog(@"In the pinch action now with scale: %f", self.pinchRecognizer.scale); NSLog(@"Gesture recognizer state is: %d", self.pinchRecognizer.state); switch (self.pinchRecognizer.state) { case 1: NSLog(@"Zoom begins, with current scale set to: %f", self.currentScale); [self.pinchRecognizer setScale:self.currentScale]; break; case 3: NSLog(@"Zoom ends, with pinch recognizer scale set to: %f", self.pinchRecognizer.scale); self.currentScale = self.pinchRecognizer.scale; default: break; } [scrollView setZoomScale:self.pinchRecognizer.scale animated:NO]; } 

所以这就是我设法解决的问题。

这是我的变化的原始:

 @interface ScrollViewZoomTestViewController () <UIScrollViewDelegate> @property (nonatomic, strong) UIImageView* imageViewPointer; // These properties are new @property (nonatomic, strong) NSMutableArray* imageViewConstraints; @property (nonatomic) BOOL imageViewConstraintsNeedUpdating; @property (nonatomic, strong) UIScrollView* scrollViewPointer; @end @implementation ScrollViewZoomTestViewController - (void)viewDidLoad { [super viewDidLoad]; UIScrollView *scrollView = [[UIScrollView alloc] init]; UIImageView *imageView = [[UIImageView alloc] init]; [imageView setImage:[UIImage imageNamed:@"pic.jpg"]]; [self.view addSubview:scrollView]; [scrollView addSubview:imageView]; scrollView.translatesAutoresizingMaskIntoConstraints = NO; imageView.translatesAutoresizingMaskIntoConstraints = NO; self.imageViewPointer = imageView; // New self.scrollViewPointer = scrollView; scrollView.maximumZoomScale = 2; scrollView.minimumZoomScale = .5; scrollView.delegate = self; NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView, imageView); NSLog(@"Current views dictionary: %@", viewsDictionary); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]]; // Saving the image view width & height constraints self.imageViewConstraints = [[NSMutableArray alloc] init]; // Constrain the image view to be the same width & height of the scroll view [_imageViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageView(scrollView)]|" options:0 metrics: 0 views:viewsDictionary]]; [_imageViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView(scrollView)]|" options:0 metrics: 0 views:viewsDictionary]]; // Add the image view constraints to the VIEW, not the scroll view [self.view addConstraints:_imageViewConstraints]; // Flag self.imageViewConstraintsNeedUpdating = YES; } 

所以在这里回顾一下,我将所有约束添加到self.view中,将设置在UIImageView上的约束保存在NSMutableArray属性中,并设置一个标志,指出UIImageView约束需要更新。

UIImageView上的这些初始约束条件将其设置为开始。 这将是UIScrollView相同的宽度和高度。 但是,这不会允许我们缩放图像视图。 它将保持与滚动视图相同的宽度/高度。 不是我们想要的。 这就是为什么我要保存限制和设置标志。 我们会在一分钟内处理好。

现在,设置缩放视图:

 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.imageViewPointer; } 

好的,现在我们需要让我们放大。 我删除了最初的UIImageView约束,并添加了一些新的约束,这次约束到UIScrollView的contentSize宽度和高度:

 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view { if(_imageViewConstraintsNeedUpdating) { // Remove the previous image view constraints [self.view removeConstraints:_imageViewConstraints]; // Replace them with new ones, this time constraining against the width & height of the scroll view's content, not the scroll view itself NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_scrollViewPointer, _imageViewPointer); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_imageViewPointer(width)]|" options:0 metrics:@{@"width" : @(_scrollViewPointer.contentSize.width)} views:viewsDictionary]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageViewPointer(height)]|" options:0 metrics:@{@"height" : @(_scrollViewPointer.contentSize.height)} views:viewsDictionary]]; self.imageViewConstraintsNeedUpdating = NO; } } @end 

我们不能在-viewDidLoad中像这样设置约束,因为图像还没有被渲染到UIImageView中,所以UIScrollView的contentSize将是{0,0}。

这对我来说似乎很不好,但它确实有效,它使用纯自动布局,我找不到更好的方法来做到这一点。 在我看来,像苹果公司需要提供一个更好的方式来缩放UIScrollView中的内容,并使用自动布局约束。