UIScrollView
内にグラフが描かれています。これは、UIView
のカスタムサブクラスをレイヤーとして使用する1つの大きなCATiledLayer
です。
UIScrollView
をズームインおよびズームアウトするとき、viewForZoomingInScrollView
からグラフを返すときと同じように、グラフのサイズを動的に変更したいと思います。ただし、グラフは新しいズームレベルで再描画されるため、次にユーザーがズームしたときに現在のビューから変換が開始されるように、変換スケールを1x1にリセットしたいと思います。変換をscrollViewDidEndZooming
でIdentityにリセットすると、シミュレーターでは機能しますが、デバイスにEXC_BAD_ACCSES
がスローされます。
これは、シミュレーターでも問題を完全に解決するわけではありません。次にユーザーがズームすると、トランスフォームがズームレベルにリセットされるため、たとえば2倍にズームした場合のように見えます。突然4倍になります。ズームを終了すると、正しい縮尺になりますが、実際のズーム動作は悪く見えます。
まず、ズーム後にグラフを1x1の標準スケールで再描画するにはどうすればよいですか。また、全体をスムーズにズームするにはどうすればよいですか。
編集: 新しい調査結果エラーは「[CALayer retainCount]: message sent to deallocated instance
」のようです
自分でレイヤーの割り当てを解除することはありません。以前は、ビューなどを削除することすらしていませんでした。このエラーは、ズームと回転でスローされていました。回転する前にオブジェクトを削除し、後で再度追加しても、例外はスローされません。これはズームのオプションではありません。
コード内のどこかでビューまたはレイヤーを意図せずに自動リリースしていないことを確認するように指示する以外は、クラッシュの解決には役立ちません。シミュレーターが自動リリースのタイミングをデバイスとは異なる方法で処理するのを見てきました(ほとんどの場合、スレッドが関係している場合)。
ただし、ビューのスケーリングは、私が遭遇したUIScrollView
の問題です。ピンチズームイベント中に、UIScrollView
はviewForZoomingInScrollView:
デリゲートメソッドで指定したビューを取得し、それに変換を適用します。この変換により、フレームごとにビューを再描画することなく、ビューをスムーズにスケーリングできます。ズーム操作の最後に、デリゲートメソッドscrollViewDidEndZooming:withView:atScale:
が呼び出され、新しい倍率でビューのより高品質なレンダリングを行う機会が与えられます。通常、ビューの変換をCGAffineTransformIdentity
にリセットしてから、ビューを新しいサイズスケールで手動で再描画することをお勧めします。
ただし、UIScrollView
はコンテンツビューの変換を監視していないように見えるため、これにより問題が発生します。次のズーム操作で、コンテンツビューの変換が全体的な倍率に設定されます。最後の倍率でビューを手動で再描画したため、表示されているスケーリングが複雑になります。
回避策として、次のメソッドを定義して、コンテンツビューにUIViewサブクラスを使用します。
- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
{
[super setTransform:newTransform];
}
- (void)setTransform:(CGAffineTransform)newValue;
{
[super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
}
ここで、previousScaleはビューのfloatインスタンス変数です。次に、ズームデリゲートメソッドを次のように実装します。
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
[contentView setTransformWithoutScaling:CGAffineTransformIdentity];
// Code to manually redraw view at new scale here
contentView.previousScale = scale;
scrollView.contentSize = contentView.frame.size;
}
これにより、コンテンツビューに送信される変換は、ビューが最後に再描画されたスケールに基づいて調整されます。ピンチズームが完了すると、通常のsetTransform:
メソッドの調整をバイパスすることにより、変換が1.0のスケールにリセットされます。これにより、ズームの完了時に鮮明なビューを描画しながら、正しいスケーリング動作が提供されるようです。
更新(2010年7月23日):iPhone OS 3.2以降では、ズームに関するスクロールビューの動作が変更されました。これで、UIScrollView
は、コンテンツビューに適用するID変換を尊重し、-scrollViewDidEndZooming:withView:atScale:
の相対的な倍率のみを提供します。したがって、上記のUIView
サブクラスのコードは、3.2より古いバージョンのiPhoneOSを実行しているデバイスにのみ必要です。
これまでのすべての回答に感謝します。これが私の解決策です。
実装IScrollViewDelegateメソッド:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return tmv;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
CGPoint contentOffset = [tmvScrollView contentOffset];
CGSize contentSize = [tmvScrollView contentSize];
CGSize containerSize = [tmv frame].size;
tmvScrollView.maximumZoomScale = tmvScrollView.maximumZoomScale / scale;
tmvScrollView.minimumZoomScale = tmvScrollView.minimumZoomScale / scale;
previousScale *= scale;
[tmvScrollView setZoomScale:1.0f];
[tmvScrollView setContentOffset:contentOffset];
[tmvScrollView setContentSize:contentSize];
[tmv setFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];
[tmv reloadData];
}
私にとっては完璧に機能します。
BR、ユージーン。
読み込み時にデフォルトのズームスケールにリセットしようとしていました。ズームすると、scrollviewは、割り当てが解除されるまで、次回も同じズームスケールになります。
-(void) viewWillAppear:(BOOL)animated {
[self.scrollView setZoomScale:1.0f];
}
ゲームを変更しました。
UIScrollViewズームがどのように(そしてなぜ)機能するかについての詳細な議論があります github.com/andreyvit/ScrollingMadness/ 。
(このリンクには、UIScrollViewをプログラムでズームする方法、フォトライブラリスタイルのページング+ズーム+スクロールをエミュレートする方法、サンプルプロジェクト、およびズームの魔法の一部をカプセル化するZoomScrollViewクラスの説明も含まれています。)
見積もり:
UIScrollViewには、「現在のズームレベル」の概念がありません。これは、UIScrollViewに含まれる各サブビューに、独自の現在のズームレベルがある場合があるためです。 UIScrollViewには、現在のズームレベルを維持するためのフィールドがないことに注意してください。ただし、サブビューをピンチズームしてから、その変換をCGAffineTransformIdentityにリセットしてからもう一度ピンチすると、サブビューの以前のズームレベルが復元されたことがわかるため、誰かがそのズームレベルを保存していることがわかっています。
実際、逆アセンブリを見ると、独自のズームレベル(_gestureInfoフィールドが指すUIGestureInfoオブジェクト内)を格納するのはUIViewです。また、zoomScale
やsetZoomScale:animated:
などの文書化されていないNiceメソッドのセットもあります。 (回転関連のメソッドもたくさんあります。近いうちに回転ジェスチャのサポートが提供される可能性があります。)
ただし、ズーム専用の新しいUIViewを作成し、実際のズーム可能なビューをその子として追加する場合は、常にズームレベル1.0から開始します。プログラムによるズームの実装は、このトリックに基づいています。
IOS 3.2および4.0以降に適した、かなり信頼できるアプローチは次のとおりです。スケーラブルビュー(スクロールビューの主サブビュー)を要求されたスケールで提供する準備をする必要があります。次に、scrollViewDidEndZooming:
で、このビューのぼやけたスケール変換バージョンを削除し、新しいスケールに描画された新しいビューに置き換えます。
必要になるだろう:
以前のスケールを維持するためのivar。それをoldScaleと呼びましょう
viewForZoomingInScrollView:
とscrollViewDidEndZooming:
の両方のスケーラブルビューを識別する方法。ここでは、タグを適用します(999)
スケーラブルビューを作成し、タグを付けて、スクロールビューに挿入するメソッド(ここではaddNewScalableViewAtScale:
と呼びます)。スケールに従ってすべてを実行する必要があることを忘れないでください。ビューとそのサブビューまたは図面のサイズを設定し、それに合わせてスクロールビューのcontentSizeのサイズを設定します。
MinimumZoomScaleおよびmaximumZoomScaleの定数。拡大縮小されたビューを詳細な拡大版に置き換えると、システムは現在の縮尺が1であると見なすため、ユーザーがビューを縮小できるように騙す必要があるため、これらが必要です。
非常によく、その後。スクロールビューを作成するときは、スケール1.0でスケーラブルビューを挿入します。これは、たとえばviewDidLoad
にある可能性があります(sv
はスクロールビューです):
[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;
次に、scrollViewDidEndZooming:
で同じことを行いますが、スケールの変化を補正します。
UIView* v = [sv viewWithTag:999];
[v removeFromSuperview];
CGFloat newscale = scale * self->oldScale;
self->oldScale = newscale;
[self addNewScalableViewAtScale:newscale];
sv.minimumZoomScale = MIN / newscale;
sv.maximumZoomScale = MAX / newscale;
ただし、Bradが提供するソリューションには1つの問題があることに注意してください。プログラムでズームインし続け(たとえばダブルタップイベントで)、しばらくしてからユーザーに手動で(ピンチアウト)ズームアウトさせた場合、実際のスケールとスケールの差があります。 UIScrollViewは追跡が大きくなりすぎるため、previousScale
はある時点でフロートの精度から外れ、最終的には予測できない動作になります。
Swift 4。*およびXcode 9.
ScrollViewズームスケールを0に設定すると、scrollViewが初期状態にリセットされます。
self.scrollView.setZoomScale(0.0, animated: true)