10

エグゼクティブ サマリー:UIScrollViewの値に望ましくない変更を加えることがあり 、contentOffsetその結果、アプリが表示中のドキュメント内の間違った場所を表示する原因となります。不要な変更は、スクロール ビューのアニメーション化された変更に関連して発生しますzoomScale

詳細: でズームアウトするときに問題が発生しCATiledLayerましたUIScrollView。はCATiledLayerpdfを保持しcontentOffset、特定の範囲内にある場合、ズームアウトするとcontentOffset、ズームが発生する前に変更されます(これがバグです)。AppleのcontentOffsetコードで変更されたようです。

問題を説明するために、Apple のサンプル アプリ ZoomingPDFViewer を変更しました。コードは github にあります: https://github.com/DirkMaas/ZoomingPDFViewer-bug

タップするとzoomScale、 を使用して 0.5 に変更されanimateWithDuration、ズームアウトします。UIScrollViewcontentOffset.y約 2700 未満または 5900 を超える場合、アニメーションzoomScaleは正常に機能します。contentOffset.yがこれらの 2 つの値の間にあるときにタップが発生するとcontentOffset.y、 は約 2700 にジャンプし (アニメーション化されません)、その後zoomScaleアニメーションが発生しますが、同時にスクロールが発生するため、アニメーションが完了すると、contentOffset.yがその場所になります。する必要があります。しかし、ジャンプはどこから来るのでしょうか?

たとえばcontentOffset.y、画面がタップされたときの値が 2000だとしますzoomScale。アニメーションは問題なく動作します。contentOffset.y変更されません。

しかしcontentOffset.y、画面をタップしたときに が 4000 の場合: はcontentOffset.yアニメーションなしで約 2700 にジャンプし、その時点からズームとスクロールが開始され、同時に発生します。アニメーションが完了すると、4000 からまっすぐにズームしたように見えるため、最終的に正しい場所に移動しますが、動作は間違っています。

UI に関する注意事項:

  • テキストは通常​​の方法で垂直方向にスクロールできます
  • テキストは通常​​の方法でピンチすることでズームインおよびズームアウトできます
  • 1 回タップすると がzoomScale0.5 に設定されます。変更はアニメーション化されます

zoomScaleが 0.5 より大きい場合、ジャンプはそれほど大きくないことに気付きました。また、のsetZoomScale:animated:代わりに使用するanimateWithDurationとバグはなくなりますが、アニメーションを連鎖させる必要があるため使用できません。

これが私がしたことの要約です(githubのコードにはこれらの変更が含まれています):

  • http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.htmlから ZoomingPDFViewer をダウンロード し、XCode で開きました。
  • ビルド設定の変更 | アーキテクチャ | ベース SDK を最新の iOS (iOS 4.3) に変更すると、ビルド設定が変更されました | GCC 4.2 - 言語 | ソースを Objective-C++ としてコンパイルする
  • プロジェクトから TestPage.pdf を削除しました
  • プロジェクトの代わりに「whoiam 5 24 cropped 3-2.pdf」を追加
  • クラスPDFScrollView *scrollView;に追加ZoomingPDFViewerViewController
  • 代わりに初期化するように変更さloadViewれましたZoomingPDFViewerViewControllerscrollViewsv
  • viewDidLoadhandleTapFrom:recognizerおよびPDFScrollview.mzoomOutに追加ZoomingPDFViewerViewController
  • コメントアウトされてscrollViewDidEndZooming:withView:atScaleおりscrollViewWillBeginZooming:withView:、画像の背景で目前の問題から気をそらすようなことをしているためです

私と一緒にいてくれてありがとう、そしてすべての助けを!

4

5 に答える 5

8

ズームについて理解するのが最も難しいことの1つは、それが常にアンカーポイントと呼ばれるポイントの周りで発生することです。それを理解する最良の方法は、ある座標系が別の座標系の上に重なっていることを想像することだと思います。Aが外側の座標系、Bが内側の座標系であるとします(Bはスクロールビューになります)。Bのオフセットが(0,0)で、スケールが1.0の場合、点B(0,0)はA(0,0)に対応し、一般にB(x、y)= A(x、y )。

さらに、Bのオフセットが(xOff、yOff)の場合、B(x、y)= A(x-xOff、y-yOff)です。繰り返しになりますが、これはズームスケールが1.0であることを前提としています。

ここで、オフセットを再び(0,0)にして、ズームしようとするとどうなるか想像してみてください。ズームしても移動しないポイントが画面上に存在する必要があり、他のすべてのポイントはそのポイントから外側に移動します。それがアンカーポイントが定義するものです。アンカーが(0,0)の場合、左下のポイントは固定されたままになり、他のすべてのポイントは右上に移動します。この場合、オフセットは同じままです。

アンカーポイントが(0.5、0.5)の場合(アンカーポイントは正規化されているため、0から1であるため、0.5は半分の幅になります)、他のすべてのポイントが外側に移動する間、中心ポイントは固定されたままになります。これは、それを反映するためにオフセットを変更する必要があることを意味します。縦向きモードのiPhoneで、スケール2.0にズームすると、アンカーポイントのx値は、画面幅の半分、320/2=160に移動します。

画面上のスクロールビューコンテンツビューの実際の位置は、オフセットとアンカーポイントの両方によって定義されます。したがって、オフセットに対応する変更を加えずに、下のレイヤーアンカーポイントを変更するだけでは、オフセットが同じであっても、ビューが別の場所にジャンプしているように見えます。

これがここでの根本的な問題だと思います。ズームをアニメーション化するとき、ズームが正しく「見える」ように、CoreAnimationは新しいアンカーポイントを選択する必要があります。これにより、オフセットが変更され、画面上の実際の表示領域がジャンプしないようになります。このプロセス全体のさまざまな時点でアンカーポイントの場所をログに記録してみてください(これは、Viewsの「layer」プロパティでアクセスする任意のビューの基になるCALayerで定義されます)。

また、きれいな写真と、おそらく私がここで与えたよりもはるかに良い状況の説明については、ここのドキュメントを参照してください:)

最後に、画面上を移動せずにレイヤーのアンカーポイントを変更するためにアプリで使用したコードのスニペットを次に示します。

-(CGPoint)setNewAnchorPointWithoutMoving:(CGPoint)newAnchor {
    CGPoint currentAnchor = CGPointMake(self.anchorPoint.x * self.contentSize.width,
                                        self.anchorPoint.y * self.contentSize.height);

    CGPoint offset = CGPointMake((1 - self.scale) * (currentAnchor.x - newAnchor.x),
                                 (1 - self.scale) * (currentAnchor.y - newAnchor.y));


    self.anchorPoint = CGPointMake(newAnchor.x / self.contentSize.width,
                                   newAnchor.y / self.contentSize.height);

    self.position = CGPointMake(self.position.x + offset.x, self.position.y + offset.y);

    return offset;
}
于 2011-09-14T20:55:48.390 に答える
3

あなたが投稿したコードで遊んでいて、何が起こっているのかがわかったと思います。コードのようにブロック アニメーションを実行すると、次のようになります。

[UIView animateWithDuration:4.0
                 animations:^ {
                     self.scrollView.zoomScale = 0.5;
                 }
                 completion:nil];

アニメーション ブロックは、実際にはアニメーションの開始時に呼び出されます。Core Animation は、実際に動いているように見せるためのレイヤーを内部的に処理します。デベロッパー センターにはアニメーション化可能なプロパティのリストがあり、zoomScale はその中にありません。

zoomScale が変更されると、scrollView は contentOffset を自動的に更新します。したがって、アニメーションが始まると、表示されるジャンプは contentOffset が新しい zoomScale に合わせて調整されることです。その後、レイヤーは適切なズーム スケールにアニメーション化されますが、ターゲットの contentOffset (つまり、ズームの終了時に contentOffset がどうあるべきか) は、ズームの開始時に既に設定されています。

これが、ズームが画面外のポイントを中心にしているように見える理由でもあります。setZoomScale:animated: を使用するか、レイヤーとオフセットを自分でアニメーション化する必要があります...

于 2011-09-22T00:00:26.013 に答える
2

viewForZoomingInScrollView 関数が scrollview を直接返すときに、ズームに多くのバグがありました。

すべてを含む scrollView 内にビューを設定し、それを viewForZoomingInScrollView に返すと、これらの奇妙な動作の多くが消えました。多分これはあなたの問題も解決するはずです:

-(void) someIntializationFunction {
    [myScrollView addSubView:self.globalContainer];
    // Now had everything in the container and not in the scrollView directly
    ....
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView; {
    return self.globalContainer;
}

もう 1 つのことですが、zoomToRect: 関数は、scrollView の zoomScale を変更するよりもはるかに信頼性が高いことがわかりました。これは、さらに計算を行う必要がある場合でも、役立つ可能性があります...

于 2011-09-19T22:57:03.293 に答える
2

scrollViewDidEndZooming:withView:atScale を使用して、ズームが完了したことを通知する必要があります。それ以外の場合は、UIScrollView のプロパティ zoomScale の使用を削除し、代わりに完全に手動でズームを実装する必要があると思います。 -iPhone/ .

于 2011-09-15T17:24:41.050 に答える
0

各スクロールイベントで contentInset や contentOffset を自分で操作する必要があると思います。アンカーポイントを操作しても役に立ちません。この質問と私の答えを見てください。

于 2013-10-25T20:57:24.810 に答える