web-dev-qa-db-ja.com

自動レイアウトによるUIScrollViewズーム

自動レイアウトを使用して、UIScrollView新しい方法 を実装しようとしています。内側のビューからスクロールビューへの制約を設定して、独自のcontentSizeを自動的に計算できるようにしました。これは魅力のように機能します。ただし、ズームインまたはズームアウトしようとすると、すべての地獄が解き放たれます。 。内面が「めちゃくちゃ」になっていると言う以外に、何が起こっているのかを正しく説明することすらできません。

この動作の例を見ることができます ここ (私のプロジェクトではありません。ズームが機能する前に、スクロールビューのmaximumZoomScaleを設定し、-viewForZoomingInScrollView:を実装する必要があります)。

他の誰かがこの動作に遭遇しましたか?現在、ズーム動作を自分で本質的に再実装せずに、UIScrollViewでズームして自動レイアウトを操作する方法はありますか?

32
phu

私が見た最良の答えは、ここに投稿されたMarkの( https://stackoverflow.com/users/1051919/mark-kryzhanouski )です: IScrollViewズームは自動レイアウトでは機能しません =。

重要なのは、スクロールビューにネストされている画像ビューをスクロールビューの親に固定する必要があることです。 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]];
4
Chris Conover

ストーリーボードに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
AMayes

完了Swiftプレイグラウンドの例

私が考えることができる最も簡単な例は、UIImageViewUIScrollViewに追加することです。これは100%コードです。プレイグラウンドにPNGを追加するだけです。私は私のと呼んだImage.png。プレイグラウンドでは、「ライブビュー」にレンダリングされたすべてのものが表示されます。ピンチズームは、Ctrlキーを押しながらクリックして画面に1本の指を置き、ドラッグすることで機能します。コンテンツが画面よりも大きく拡大されるまで、パンは機能しません。画像をダブルタップして、1倍と3倍のスケールを切り替えます。

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()