38

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

这尤其令人沮丧,因为 iOS 6 发行说明确实让我相信,当在这里http://developer.apple.com/library/ios/#releasenotes/General/RN-写关于“纯自动布局方法”的文章时应该这样做iOSSDK-6_0/_index.html

我查看了 WWDC 2012 的第 202、228 和 232 场幻灯片,但没有看到答案。

我在互联网上看到的唯一一个专门针对这个问题的问题是UIScrollView zooming with Auto Layout,但它没有提供问题的代码,也没有答案。

这个用户https://stackoverflow.com/users/341994/matt对 UIScrollView 自动布局问题给出了很多很好的回应,甚至链接到 git hub 上的代码,但我在那里找不到任何可以回答这个问题的东西。

我试图将这个问题归结为绝对最低限度以使其清楚。

我创建了一个带有情节提要的新单视图应用程序,并且在界面构建器中没有进行任何更改。

我在项目“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 发行说明中提供的示例代码一样,只是做了最简单的实现缩放。

那么,问题呢?

当您运行此应用程序并在滚动视图中平移时,一切都很好。但是当您缩放时,问题很明显,图像来回闪烁,并且每次缩放时图像在滚动视图中的位置都会变得更加错误。

看起来 imageView 的内容偏移量正在进行战斗,似乎每次“缩放”都会被两个不同的东西设置为不同的值。(imageView 的内容偏移属性的 NSLog 似乎证实了这一点)。

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

请帮忙。

4

6 回答 6

14

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

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

解决方案

将您的子视图连接到外部视图。换句话说,就是嵌入了滚动视图的视图。

并以以下方式应用约束,我已经成功了:

[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]];
于 2013-03-11T22:57:53.430 回答
10

发生的问题是缩放过程中图像视图的位置变化。在缩放过程中,imageview 的原点位置将变为负值。我相信这就是为什么会发生生涩的运动。同样,在缩放完成后,imageView 仍然在错误的位置,这意味着滚动会出现偏移。

如果你-(void) scrollViewDidZoom:(UIScrollView *)scrollView在这段时间内实现并记录 UIImageView 的框架,你可以看到它的原点发生了变化。

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

除了在缩放时更改 contentView 的框架

-(void) scrollViewDidZoom:(UIScrollView *)scrollView {
    CGRect cFrame = self.contentView.frame;
    cFrame.origin = CGPointZero;
    self.contentView.frame = cFrame;
}
于 2013-03-18T04:40:18.147 回答
4

这些解决方案都很有效。这是我所做的,不需要任何黑客或子类,使用这个设置:

[view]
  [scrollView]
    [container]
      [imageView]
      [imageView2]
  1. scrollView在 IB 中,将to的顶部、前导、底部和尾部连接起来view
  2. container连接到的顶部、前导、底部和尾随scrollView
  3. 将 center-x 和 center-ycontainer连接到 center-x 和 center-y 上,scrollView 并将其标记为在 build time 上删除。这只需要使 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 倍。

于 2014-01-06T10:23:43.117 回答
1

假设你在情节提要“UIImageView里面” UIScrollView“里面UIView”。

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

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

然后实现这段代码:

@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;
}
于 2013-08-12T14:33:23.593 回答
0

尝试仅使用 scrollView 从情节提要的项目中实现缩放时,我遇到了同样的问题。

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

将捏合手势识别器引入场景后,您确实需要 action 和 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”来跟踪缩放并在再次开始缩放时设置捏合手势识别器缩放来解决此问题。您需要使用手势识别器的 state 属性:

- (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];
}
于 2013-06-10T10:03:06.043 回答
0

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

这是我的更改的原件:

@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,保存UIImageViewNSMutableArray属性中设置的约束,并设置UIImageView约束需要更新的标志。

这些对工作的初始限制从UIImageView设置它开始。它将与UIScrollView. 但是,这不允许我们缩放图像视图。它将保持与滚动视图相同width/ 。height不是我们想要的。这就是我保存约束并设置标志的原因。我们会在一分钟内解决这个问题。

现在,设置缩放视图:

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

好的,所以现在我们需要实际允许我们缩放。我正在删除初始UIImageView约束并添加一些新约束,这一次约束到UIScrollView's contentSize width& height

- (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's contentSizewill be {0,0}

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

于 2014-04-03T20:50:25.120 回答