UIScrollView zooming with Auto Layout

后端 未结 3 2091
渐次进展
渐次进展 2020-12-08 14:12

I\'m trying to implement a UIScrollView the New Way, using Auto Layout. I\'ve set up constraints from the inner view to the scroll view so that it can compute i

相关标签:
3条回答
  • 2020-12-08 14:26

    Without adding an imageView in the storyboard, I've found the following works perfectly:

    -(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;
    }
    
    0 讨论(0)
  • 2020-12-08 14:30

    Complete Swift Playground Example

    The simplest example I can think of is adding a UIImageView to a UIScrollView. This is 100% in code, you'll just need to add a PNG to the Playground. I called mine Image.png. In a Playground you'll see the whole thing rendered in the 'Live View'. Pinch-zoom works using a Ctrl-click to place one finger on the screen and then dragging around. Until the content is zoomed in bigger than the screen panning will not work. Double-tap the image to toggle between 1x and 3x scale.

    Based upon the Apple's Technical Note TN2154: UIScrollView and Autolayout

    Gotcha

    You'll find the whole thing very frustrating if your content is not bigger than the screen size. If your content completely fits on screen nothing will happen. That's why you must get zooming to work too. If you want to prove to yourself it works, test with a really big image (bigger than the window).

    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()
    
    0 讨论(0)
  • 2020-12-08 14:43

    The best answer that I have seen is Mark's (https://stackoverflow.com/users/1051919/mark-kryzhanouski), posted here: UIScrollView Zoom Does Not Work With Autolayout.

    The crux of it is that you have to anchor the image view that is nested in the scroll view, to the parent of the scroll view. Despite the guidance in the iOS 6 release notes, it is not intuitive to me what view is "floating" over what. In this case, the scrolling view is just a single image view.

    I did do a lot of experimentation with this, hoping to find an all-IB approach and found none. You can still generate the view hierarchy in IB, but you still have to programatically add constraints. You can delete some or all of the default constraints (mainly just to appease the constraint-conflict warnings), but you always need Mark's code to tie the image view to the parent of the scroll view, the grand-parent of the image view.

    It seems like it should be simpler than this - it "should just work" but:

    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 讨论(0)
提交回复
热议问题