達成したい効果について説明します。次に、これを現在どのように実装しようとしているのか、そして現状の動作の何が悪いのかについて詳しく説明します。私が見たが、まったく機能しなかった別のアプローチについても触れます。
最も関連性の高いコードが質問の下部にインラインで表示され、すばやくアクセスできます。 ソースのZipをダウンロードまたはプロジェクトを-として取得できます Mercurial RepositoryBitBucketで。プロジェクトには、以下の回答からの修正が組み込まれています。壊れたバージョンを最初に提供したい場合は、 "initial-buggy-version"のタグが付けられます
このプロジェクトは、効果が実行可能かどうかを評価するための最小限の概念実証/スパイクであるため、かなり軽くてシンプルです!
アプリは、垂直テーブルを形成する多数の個別の情報行を表示します。テーブルはユーザーが垂直方向にスクロールできます。これはUITableView
の標準的な動作であり、UICollectionView
も使用できます。ただし、アプリはピンチスケーリングもサポートする必要があります。テーブルでズームをピンチすると、allの行がつぶれます。伸ばすと、allの線が引き離されます。
私の概念実証では、個々のセルのサイズは変更されていません。それらのセルは、より近くまたはより離れて再配置されています。これは意図的なものです。アイデアの実現可能性を検証することが重要だとは思いません。
現在のアプリがどのようにズームアウトおよびズームアウトされているかを示す画面グラブは次のとおりです。
私はカスタムUICollectionView
サブクラスでUICollectionViewLayout
を使用しています。レイアウトは、UICollectionViewCells
を画面の真ん中に下がるうねりの良い正弦波に配置します。各UICollectionViewCell
は、UILabel
行を保持するindexPath
のコンテナーにすぎません。
UICollectionViewLayout
サブクラスには、説明する各セル間の垂直方向の間隔をUICollectionView
に設定するパラメーターがあり、これを調整することで、テーブルを必要に応じて垂直方向に押しつぶしたり垂直方向に拡大したりできます。
私のUICollectionViewController
サブクラスにはUIPinchGestureRecognizer
があります。認識機能がスケールの変更を検出すると、UICollectionView
のレイアウトの垂直セル間隔がそれに応じて変更されます。
これ以上考慮しなければ、スケーリングは、タッチジェスチャの中心ではなく、コンテンツの上部から発生します。 UICollectionView
のcontentOffset
プロパティは、この機能を提供するためにピンチ中に調整されます。
ジェスチャーレコグナイザーは、つまむときに発生するドラッグに対応する必要もあります。これは、UICollectionView
のcontentOffset
を変更することによっても処理されます。いくつかの追加コードでは、ジェスチャーに指を追加したり、ジェスチャーから指を削除したりするときに、タッチジェスチャーの中心点を変更できます。
UICollectionView
はUIScrollView
サブクラスであるため、独自に追加したUIPanGestureRecognizer
と相互作用する独自のUIPinchGestureRecogniser
があることに注意してください。これが問題の原因かどうかはわかりません。
ピンチジェスチャー中にUICollectionView
の組み込みのスクロールを無効にするコードを追加しましたが、それほど大きな違いはないようです。 gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
を使用してUIPinchGestureRecognizer
を組み込みのUIPanGestureRecognizer
で失敗させようとしましたが、これによりピンチ認識機能がまったく動作しなくなったようです。これが私が愚かなのか、iOSのバグなのかはわかりません。
前述のように、現在のUICollectionViewCell
sはサイズ変更されません。それらは単に再配置されます。これは意図的なものです。この概念を検証することは重要ではないと思います。
動作ビットは非常によく機能します。テーブルを上下にドラッグできます。ドラッグ中に、指を追加してピンチを開始し、指を離してドラッグを続行し、追加してピンチするなどを行うことができます。すべて非常にスムーズです。オリジナルのiPhone 5では、ピンチとパンをスムーズにサポートし、画面上に200以上のビューを表示します。
ビューの上部または下部が画面上にあるときにピンチインおよびピンアウトしようとすると、すべてが少し気が狂います。
ピンチジェスチャーの間、これら2つは互いに戦うため、コンテンツが上下に激しくちらつきます(これは絶対に必要ありません!)。
UICollectionView
のデフォルトのスクロールは、スクロール中に手を離すと減速し、外側にスクロールするとスムーズに跳ね返ります。現在、これらはまったく処理されていません。
UICollectionView
、UIScrollView
であることは、ズームをサポートするように正しく設定されている場合、組み込みのUIPinchGestureRecogniser
が必要です。自分のUIPinchGestureRecogniser
の代わりにこれを利用できるかどうか疑問に思いました。最小と最大のスケールを設定し、コントローラーのピンチハンドラーを追加して、これを設定しようとしました。ただし、viewForZoomingInScrollView:
の実装から何を返す必要があるのか本当に理解できないので、[[UIView alloc] initWithFrame: [[self collectionView] bounds]]
でダミーのビューを作成しています。それはスクロールビューを1行に「折りたたむ」ようにしますが、それは私が求めているものではありません!
これは長い質問ですので、読んでいただきありがとうございます。回答にご協力いただける場合は、さらによろしくお願いいたします。言ったことや追加したことの多くが無関係であるとすいません。
// STViewController.m
#import "STViewController.h"
#import "STDataColumnsCollectionViewLayout.h"
#import "STCollectionViewLabelCell.h"
@interface STViewController () <UIGestureRecognizerDelegate>
@property (nonatomic, assign) CGFloat pinchStartVerticalPeriod;
@property (nonatomic, assign) CGFloat pinchNormalisedVerticalPosition;
@property (nonatomic, assign) NSInteger pinchTouchCount;
-(void) handlePinch: (UIPinchGestureRecognizer *) pinchRecogniser;
@end
@implementation STViewController
-(void) viewDidLoad
{
[[self collectionView] registerClass: [STCollectionViewLabelCell class] forCellWithReuseIdentifier: [STCollectionViewLabelCell className]];
UICollectionView *const collectionView = [self collectionView];
[collectionView setAllowsSelection: NO];
[_pinchRecogniser addTarget: self action: @selector(handlePinch:)];
[_pinchRecogniser setDelegate: self];
[_pinchRecogniser setCancelsTouchesInView:YES];
[[self view] addGestureRecognizer: _pinchRecogniser];
}
#pragma mark -
-(NSInteger) collectionView: (UICollectionView *)collectionView numberOfItemsInSection: (NSInteger)section
{
return 800;
}
-(UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
STCollectionViewLabelCell *const cell = [[self collectionView] dequeueReusableCellWithReuseIdentifier: [STCollectionViewLabelCell className] forIndexPath: indexPath];
[[cell label] setText: [NSString stringWithFormat: @"%d", [indexPath row]]];
return cell;
}
#pragma mark -
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
#pragma mark -
-(void) handlePinch: (UIPinchGestureRecognizer *) pinchRecogniser
{
UICollectionView *const collectionView = [self collectionView];
STDataColumnsCollectionViewLayout *const layout = (STDataColumnsCollectionViewLayout *)[self collectionViewLayout];
if(([pinchRecogniser state] == UIGestureRecognizerStateBegan) || ([pinchRecogniser numberOfTouches] != _pinchTouchCount))
{
const CGFloat normalisedY = [pinchRecogniser locationInView: collectionView].y / [layout collectionViewContentSize].height;
_pinchNormalisedVerticalPosition = normalisedY;
_pinchTouchCount = [pinchRecogniser numberOfTouches];
}
switch ([pinchRecogniser state])
{
case UIGestureRecognizerStateBegan:
{
NSLog(@"Began");
_pinchStartVerticalPeriod = [layout verticalPeriod];
[collectionView setScrollEnabled: NO];
break;
}
case UIGestureRecognizerStateChanged:
{
NSLog(@"Changed");
STDataColumnsCollectionViewLayout *const layout = (STDataColumnsCollectionViewLayout *)[self collectionViewLayout];
const CGFloat newVerticalPeriod = _pinchStartVerticalPeriod * [pinchRecogniser scale];
[layout setVerticalPeriod: newVerticalPeriod];
[[self collectionViewLayout] invalidateLayout];
const CGPoint dragCenter = [pinchRecogniser locationInView: [collectionView superview]];
const CGFloat currentY = _pinchNormalisedVerticalPosition * [layout collectionViewContentSize].height;
[collectionView setContentOffset: CGPointMake(0, currentY - dragCenter.y) animated: NO];
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
[collectionView setScrollEnabled: YES];
}
default:
break;
}
}
@end
上記のコードに対するいくつかの非常にマイナーな調整により、機能しないもの1と機能しないものが解決されました2 質問の中で。
viewDidLoad
のUICollectionViewController
メソッドに次の行を追加しました。
[collectionView setMinimumZoomScale: 0.25];
[collectionView setMaximumZoomScale: 4];
また、サンプルプロジェクトを更新して、テキストラベルの代わりにビューが小さな円で構成されるようにしました。ズームインおよびズームアウトすると、これらのサイズが変更されます。以下は、現在の状態です(ズームアウトとズームイン)。
ズーム中、円のビューは再描画されず、ズーム前のサイズから補間されます。再描画は、ズームが終了するまで延期されます。これは、数回のズームイン後の様子のキャプチャです。
ズーム中に再描画をバックグラウンドスレッドで実行して、アーティファクトが目立たなくなるようにするのは素晴らしいことですが、それはこの質問の範囲外であり、まだ取り組んでいません。
Bit Bucketでプロジェクト全体を修正して見つけることができるので、 ファイルを取得 できます。
この質問に答えることで、UIScrollView
ズームについて多くの新しい確信が持てることを望んでいました。私はしません。
私がUIScrollViewについて読んだことから、この「修正」は何の違いももたらさないはずであり、いずれにしても最初からすでに機能しているはずです。
UIScrollView
は、viewForZoomingInScrollView:
を実装するデリゲートを指定するまで、スクロールを有効にすることはできません。