10

私の iPhone アプリでは、画面上の大きな画像をズーム/パンする機能をユーザーに提供する必要があります。これは非常に簡単です。私は を使用しUIScrollView、最大/最小スケール係数を設定し、ズーム/パンが期待どおりに機能します。ここからが興味深いところです。画像はサーバーから受信した動的なものです。任意の次元を持つことができます。画像が最初に読み込まれると、(必要に応じて) に完全に収まるように縮小さUIScrollViewれ、スクロール ビューの中央に配置されます。スクリーンショットは次のとおりです。

ここに画像の説明を入力

画像の比率はスクロール ビューの比率とは異なるため、画像が中央に配置されるように、画像の上下に空白が追加されています。ただし、画像のズームを開始すると、実際の画像はスクロールビューのビューポート全体を埋めるのに十分な大きさになるため、上部/下部の白いパディングはもう必要ありませんが、このスクリーンショットからわかるように、そこに残ります:

ここに画像の説明を入力

これはUIImageView、画像を含む画像が全体を埋めるように自動的にサイズ変更されUIScrollView、ズームすると比例して大きくなるという事実によるものだと思います。スケール モードが に設定されていAspect Fitます。 UIScrollViewのデリゲートviewForZoomingInScrollViewは単に画像ビューを返します。

方法で再計算して再設定UIScrollViewし、contentSize画像ビューのサイズを試みましたscrollViewDidEndZooming:

CGSize imgViewSize = imageView.frame.size;
CGSize imageSize = imageView.image.size;

CGSize realImgSize;
if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height) {
    realImgSize = CGSizeMake(imgViewSize.width, imgViewSize.width / imageSize.width * imageSize.height);
}
else {
    realImgSize = CGSizeMake(imgViewSize.height / imageSize.height * imageSize.width, imgViewSize.height);
}
scrollView.contentSize = realImgSize;

CGRect fr = CGRectMake(0, 0, 0, 0);
fr.size = realImgSize;
imageView.frame = fr;

ただし、これは事態を悪化させるだけでした (境界はまだありますが、垂直方向のパンは機能しません)。

その空白が不要になったときに自動的に縮小し、ズームイン中に再びインクリメントする方法はありますか? この作業は で行う必要があると思われますがscrollViewDidEndZooming、そのコードがどのようなものである必要があるのか​​よくわかりません。

4

6 に答える 6

18

素晴らしい!

コードをありがとう:)

動作を改善するために少し変更したので、これに追加すると思いました。

// make the change during scrollViewDidScroll instead of didEndScrolling...
-(void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    CGSize imgViewSize = self.imageView.frame.size;
    CGSize imageSize = self.imageView.image.size;

    CGSize realImgSize;
    if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height) {
        realImgSize = CGSizeMake(imgViewSize.width, imgViewSize.width / imageSize.width * imageSize.height);
    }
    else {
        realImgSize = CGSizeMake(imgViewSize.height / imageSize.height * imageSize.width, imgViewSize.height);
    }

    CGRect fr = CGRectMake(0, 0, 0, 0);
    fr.size = realImgSize;
    self.imageView.frame = fr;

    CGSize scrSize = scrollView.frame.size;
    float offx = (scrSize.width > realImgSize.width ? (scrSize.width - realImgSize.width) / 2 : 0);
    float offy = (scrSize.height > realImgSize.height ? (scrSize.height - realImgSize.height) / 2 : 0);

    // don't animate the change.
    scrollView.contentInset = UIEdgeInsetsMake(offy, offx, offy, offx);
}
于 2013-07-04T09:07:51.097 に答える
16

これは、タブバーまたはナビゲーションバーの組み合わせ、または両方なしで、半透明かどうかにかかわらず、普遍的に機能する私のソリューションです。

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
  // The scroll view has zoomed, so you need to re-center the contents
  CGSize scrollViewSize = [self scrollViewVisibleSize];
  // First assume that image center coincides with the contents box center.
  // This is correct when the image is bigger than scrollView due to zoom
  CGPoint imageCenter = CGPointMake(self.scrollView.contentSize.width/2.0,
                                    self.scrollView.contentSize.height/2.0);

  CGPoint scrollViewCenter = [self scrollViewCenter];

  //if image is smaller than the scrollView visible size - fix the image center accordingly
  if (self.scrollView.contentSize.width < scrollViewSize.width) {
    imageCenter.x = scrollViewCenter.x;
  }

  if (self.scrollView.contentSize.height < scrollViewSize.height) {
    imageCenter.y = scrollViewCenter.y;
  }

  self.imageView.center = imageCenter;
}


//return the scroll view center
- (CGPoint)scrollViewCenter {
  CGSize scrollViewSize = [self scrollViewVisibleSize];
  return CGPointMake(scrollViewSize.width/2.0, scrollViewSize.height/2.0);
}


// Return scrollview size without the area overlapping with tab and nav bar.
- (CGSize) scrollViewVisibleSize {
  UIEdgeInsets contentInset = self.scrollView.contentInset;
  CGSize scrollViewSize = CGRectStandardize(self.scrollView.bounds).size;
  CGFloat width = scrollViewSize.width - contentInset.left - contentInset.right;
  CGFloat height = scrollViewSize.height - contentInset.top - contentInset.bottom;
  return CGSizeMake(width, height);
}

スイフト5:

public func scrollViewDidZoom(_ scrollView: UIScrollView) {
    centerScrollViewContents()
}

private var scrollViewVisibleSize: CGSize {
    let contentInset = scrollView.contentInset
    let scrollViewSize = scrollView.bounds.standardized.size
    let width = scrollViewSize.width - contentInset.left - contentInset.right
    let height = scrollViewSize.height - contentInset.top - contentInset.bottom
    return CGSize(width:width, height:height)
}

private var scrollViewCenter: CGPoint {
    let scrollViewSize = self.scrollViewVisibleSize()
    return CGPoint(x: scrollViewSize.width / 2.0,
                   y: scrollViewSize.height / 2.0)
}

private func centerScrollViewContents() {
    guard let image = imageView.image else {
        return
    }

    let imgViewSize = imageView.frame.size
    let imageSize = image.size

    var realImgSize: CGSize
    if imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height {
        realImgSize = CGSize(width: imgViewSize.width,height: imgViewSize.width / imageSize.width * imageSize.height)
    } else {
        realImgSize = CGSize(width: imgViewSize.height / imageSize.height * imageSize.width, height: imgViewSize.height)
    }

    var frame = CGRect.zero
    frame.size = realImgSize
    imageView.frame = frame

    let screenSize  = scrollView.frame.size
    let offx = screenSize.width > realImgSize.width ? (screenSize.width - realImgSize.width) / 2 : 0
    let offy = screenSize.height > realImgSize.height ? (screenSize.height - realImgSize.height) / 2 : 0
    scrollView.contentInset = UIEdgeInsets(top: offy,
                                           left: offx,
                                           bottom: offy,
                                           right: offx)

    // The scroll view has zoomed, so you need to re-center the contents
    let scrollViewSize = scrollViewVisibleSize

    // First assume that image center coincides with the contents box center.
    // This is correct when the image is bigger than scrollView due to zoom
    var imageCenter = CGPoint(x: scrollView.contentSize.width / 2.0,
                              y: scrollView.contentSize.height / 2.0)

    let center = scrollViewCenter

    //if image is smaller than the scrollView visible size - fix the image center accordingly
    if scrollView.contentSize.width < scrollViewSize.width {
        imageCenter.x = center.x
    }

    if scrollView.contentSize.height < scrollViewSize.height {
        imageCenter.y = center.y
    }

    imageView.center = imageCenter
}

これまでにSOで見つけた他の何よりも優れている理由:

  1. ズームされた画像ビューには変換が適用されているため、画像ビューの UIView フレーム プロパティを読み取ったり変更したりしません。非恒等変換が適用されたときにビュー サイズを移動または調整する方法について Apple が述べていることをここで参照してください。

  2. バーの半透明性が導入された iOS 7 以降、システムはスクロール ビューのサイズ、スクロール コンテンツのインセット、およびスクロール インジケーターのオフセットを自動調整します。したがって、コードでもこれらを変更しないでください。

参考までに: Xcode インターフェイス ビルダーには、この動作を切り替えるためのチェック ボックス (既定で設定されています) があります。ビューコントローラーの属性で見つけることができます:

自動スクロール ビュー調整

View Controller の完全なソース コードは、こちらで公開されています。

また、 Xcode プロジェクト全体をダウンロードして、スクロール ビューの制約の設定を確認し、最初のコントローラー ポインターを次のいずれかのパスに移動して、ストーリーボードの 3 つの異なるプリセットを試してみることもできます。

  1. 半透明のタブ バーとナビゲーション バーの両方を表示します。
  2. 不透明なタブとナビゲーション バーの両方で表示します。
  3. バーがまったくないビュー。

すべてのオプションは、同じ VC 実装で正しく機能します。

于 2016-02-28T09:06:02.587 に答える
7

わかったと思います。解決策は、デリゲートのメソッドを使用し、scrollViewDidEndZoomingそのメソッドでcontentInset画像のサイズに基づいて設定することです。メソッドは次のようになります。

- (void)scrollViewDidEndZooming:(UIScrollView *)aScrollView withView:(UIView *)view atScale:(float)scale {
    CGSize imgViewSize = imageView.frame.size;
    CGSize imageSize = imageView.image.size;

    CGSize realImgSize;
    if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height) {
        realImgSize = CGSizeMake(imgViewSize.width, imgViewSize.width / imageSize.width * imageSize.height);
    }
    else {
        realImgSize = CGSizeMake(imgViewSize.height / imageSize.height * imageSize.width, imgViewSize.height);
    }

    CGRect fr = CGRectMake(0, 0, 0, 0);
    fr.size = realImgSize;
    imageView.frame = fr;

    CGSize scrSize = scrollView.frame.size;
    float offx = (scrSize.width > realImgSize.width ? (scrSize.width - realImgSize.width) / 2 : 0);
    float offy = (scrSize.height > realImgSize.height ? (scrSize.height - realImgSize.height) / 2 : 0);
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.25];
    scrollView.contentInset = UIEdgeInsetsMake(offy, offx, offy, offx);
    [UIView commitAnimations];
}

インセットの設定にアニメーションを使用していることに注意してください。そうしないと、インセットが追加されたときに画像がスクロールビュー内にジャンプします。アニメーションでは、中央にスライドします。アプリをiphone3で実行する必要があるため、アニメーションブロックの代わりにを使用UIView beginAnimationしています。commitAnimation

于 2012-12-28T13:09:35.253 に答える