UIScrollView масштабирование с автоматической компоновкой

Я пытаюсь реализовать UIScrollView Новый способ с помощью автоматической компоновки. Я установил ограничения из внутреннего представления в представление прокрутки, чтобы он мог автоматически вычислить свой собственный contentSize, и это работает как шарм, за исключением того, что весь ад разрывается, когда я пытаюсь увеличить или уменьшить масштаб. Я не могу даже правильно описать, что происходит, кроме того, что внутреннее представление "перепуталось".

Здесь вы можете увидеть пример этого (а не мой проект, вам нужно установить прокрутку maximumZoomScale и реализовать -viewForZoomingInScrollView: до того, как масштабирование будет работать).

Кто-нибудь еще сталкивается с этим поведением? Есть ли какой-либо способ получить масштабирование в UIScrollView для работы с автоматической компоновкой без существенного повторного выполнения поведения масштабирования?

Ответ 1

Лучший ответ, который я видел, - Mark (https://stackoverflow.com/users/1051919/mark-kryzhanouski), размещенный здесь: UIScrollView Zoom не работает С автоопределением.

Суть его в том, что вы должны привязать вид изображения, который вложен в представление прокрутки, родительскому представлению прокрутки. Несмотря на руководство в примечаниях к выпуску 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]];

Ответ 2

Без добавления 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;
}

Ответ 3

Полный пример игровой площадки Swift

Простейшим примером, о котором я могу думать, является добавление UIImageView к UIScrollView. Это 100% кода, вам просто нужно добавить PNG к игровой площадке. Я назвал мой Image.png. На игровой площадке вы увидите все, что отображается в "Live View". Pinch-zoom работает с помощью Ctrl-клика, чтобы поместить один палец на экран, а затем перетащить его. Пока контент не будет увеличен, экранное панорамирование не будет работать. Дважды нажмите изображение, чтобы переключиться между шкалой 1x и 3x.

На основе Apple Техническая нота TN2154: UIScrollView и 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()