2012-12-25 55 views
29

我试图使用自动布局实现UIScrollViewNew Way。我已经设置了从内部视图到滚动视图的约束条件,以便它可以自动计算自己的contentSize,而且它的功能就像一个魅力 - 除了当我试图放大或缩小时,所有地狱崩溃。我甚至无法恰当地描述发生的情况,只能说内部视图会“搞砸”。UIScrollView使用自动布局缩放

你可以看到这种行为的例子here(不是我的项目;你必须设置滚动视图的maximumZoomScale和实施-viewForZoomingInScrollView:之前缩放将工作)。

有没有其他人遇到过这种行为?目前有什么方法可以缩放UIScrollView来处理自动布局,而不必自己重新实现缩放行为?

+0

我知道你的意思。我有同样的问题。你弄明白了吗?这似乎与约束有关,我尝试在'scrollViewWillBeginZooming:withView:'中移除约束,并将它们放回到'scrollViewDidEndZooming:withView:atScale:'中,并且在缩放期间效果更好,但是在缩放内容在错误的地方。 – devguydavid

+1

@david:不,我从来没有解决过这个问题。我的结论是,UIKit的各个部分与Auto Layout不兼容,因此我在这些情况下停止使用Auto Layout(特别是'UIScrollView'和'UIPageViewController')。 – phu

+0

我也有这个问题。我有一个带有嵌入式图像视图的滚动视图,并且试图只允许简单缩放图像,并且它是全部时髦的,扭曲整个图像视图,同时在缩小视图的同时跳跃大小。希望有人会为此找到解决办法。 – JimmyJammed

回答

3

没有情节板中添加ImageView的,我发现了以下工作完美:

-(UIImageView *)imageView 
{ 
    if (!_imageView) _imageView = [[UIImageView alloc] initWithFrame:CGRectZero]; 
    return _imageView; 
} 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self.scrollView addSubview:self.imageView]; 
    // Set the min and max: 
    self.scrollView.minimumZoomScale = 0.2; 
    self.scrollView.maximumZoomScale = 5.0; 
    self.scrollView.delegate = self; 

    // Set the content: 
    self.scrollView.zoomScale = 1.0; // reset zoomScale for new image 
    self.scrollView.contentSize = CGSizeMake(image.size.width/2, image.size.height/2); 
    self.imageView.frame = CGRectMake(0, 0, image.size.width/2, image.size.height/2); 
    self.imageView.image = image; 
} 

-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView 
{ 
    return self.imageView; 
} 
+5

您正在手动设置滚动视图的'contentSize',而不是自动布局约束,这是问题的要点。查看我发布的第一个链接,了解我的意思(搜索短语“纯自动布局方法”)。 – phu

+0

“纯度”是什么意思?我正在提供一个解决方案。 – AMayes

+0

它确实帮了我,谢谢! – Jake

4

我见过最好的答案是马克(https://stackoverflow.com/users/1051919/mark-kryzhanouski),贴在这里:UIScrollView Zoom Does Not Work With Autolayout

问题的关键在于,您必须将嵌套在滚动视图中的图像视图锚定到滚动视图的父级。尽管iOS 6发行说明中提供了指导,但对于我而言,什么观点“浮动”在内容上并不直观。在这种情况下,滚动视图只是一个图像视图。

我做了很多这方面的实验,希望找到一个全IB方法,但没有找到。您仍然可以在IB中生成视图层次结构,但仍然需要以编程方式添加约束。您可以删除部分或全部默认约束(主要仅用于解决约束冲突警告),但您始终需要使用Mark的代码将图像视图绑定到滚动视图的父级(即图像视图的父级)。

现在看来似乎应该比这个更简单 - 它“应该只是工作”,而是:

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

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

当你说'你必须锚定嵌套在滚动视图中的图像视图到滚动视图的父级'时,是否有任何要执行此操作的动作?因为我已经编程设置约束为马克,你说过,但我仍然无法使它工作。 – Yaman

1

完整斯威夫特游乐场举例

我能想到的最简单的例子是添加一个UIImageViewUIScrollView。这是100%的代码,你只需要添加一个PNG到游乐场。我打电话给我的Image.png。在Playground中,您会看到'Live View'中呈现的整个事物。捏放大使用按住Ctrl单击将一个手指放在屏幕上,然后拖动。直到内容放大到大于屏幕平移不起作用。双击图像以在1x和3x比例之间切换。

基于苹果的Technical Note TN2154: UIScrollView and Autolayout

疑难杂症

你会发现整个事情很令人沮丧,如果你的内容并不比屏幕尺寸更大。如果您的内容完全符合屏幕,则不会发生任何事情这就是为什么你必须变焦才能工作。如果你想向自己证明它是有效的,那么测试一个非常大的图像(比窗口大)。

import UIKit 
import PlaygroundSupport 

enum TapToggle { 
    case Regular, Large 
} 

class ScrollingViewController : UIViewController 
{ 
    var tapToggle: TapToggle = .Large 
    var scrollView: UIScrollView? 
    var imageView: UIImageView? 

    override func viewDidLoad() 
    { 
     let image = UIImage(named: "Image") 
     let imageView = UIImageView(image: image) 
     imageView.translatesAutoresizingMaskIntoConstraints = false 
     imageView.backgroundColor = .white 
     imageView.isUserInteractionEnabled = true 

     let scrollView = UIScrollView() 
     scrollView.minimumZoomScale = 0.5 
     scrollView.maximumZoomScale = 10.0 
     scrollView.delegate = self 
     scrollView.translatesAutoresizingMaskIntoConstraints = false 
     scrollView.addSubview(imageView) 
     let imageViewKey = "imageView" 
     let imageViews = [imageViewKey: imageView] 
     scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[\(imageViewKey)]|", options: [], metrics: nil, views: imageViews)) 
     scrollView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[\(imageViewKey)]|", options: [], metrics: nil, views: imageViews)) 
     self.imageView = imageView 

     scrollView.backgroundColor = .white 
     self.view.addSubview(scrollView) 

     let scrollViewKey = "scrollView" 
     let scrollViews = [scrollViewKey: scrollView] 
     self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[\(scrollViewKey)]|", options: [], metrics: nil, views: scrollViews)) 
     self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[\(scrollViewKey)]|", options: [], metrics: nil, views: scrollViews)) 

     self.scrollView = scrollView 

     let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didDoubleTap(sender:))) 
     tapGesture.numberOfTapsRequired = 2 
     self.imageView?.addGestureRecognizer(tapGesture) 
    } 

    @objc 
    public func didDoubleTap(sender: AnyObject) 
    { 
     switch self.tapToggle { 
     case .Regular: 
      self.scrollView?.zoomScale = 1.0 
      self.tapToggle = .Large 
     case .Large: 
      self.scrollView?.zoomScale = 3.0 
      self.tapToggle = .Regular 
     } 
    } 
} 

extension ScrollingViewController: UIScrollViewDelegate 
{ 
    func viewForZooming(in scrollView: UIScrollView) -> UIView? { 
     return self.imageView 
    } 

    func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) 
    { 
     print("\(scale)") 
    } 
} 

PlaygroundPage.current.needsIndefiniteExecution = true 
PlaygroundPage.current.liveView = ScrollingViewController()