UIGestures を使用して、UIScrollView と同様のズームとパンを実行しようとしています。
私のビューは、ON/OFF セルのマトリックスから描画され、何千ものセルをサポートできる必要があります。drawRect: メソッドは、マトリックス座標を画面座標に変換します。ビューには、ズーム量のプロパティと、オフセットを保持する CGPoint があります。
以下のズームとパンが分かればいいと思います。以下のコードの壁で申し訳ありませんが、これは私のより複雑なプログラムを反映した完全な実装を表しています。
現在、ズームはすべてをスケーリングしますが、UIScrollView のズームと同じように、ズーム自体を中央に配置する方法が必要です。
パンニングがまったく機能しません。
ZoomView.h
ZoomView はブール値のマトリックスを描画します。
#import <Foundation/Foundation.h>
#import "ZoomModel.h"
@interface ZoomView : UIView
{
ZoomModel *m;
}
@property (nonatomic) float zoomScale;
@property (nonatomic) CGPoint offset;
- (id)initWithFrame:(CGRect)frame
andModel:(ZoomModel *)model;
- (BOOL)checkCellAt:(float)x
andY:(float)y;
- (CGSize)resize;
@end
ZoomView.m
drawRect: メソッドは、どのマトリックス要素を画面の可視部分に配置するかを決定するための計算を行います。画面の表示部分は、zoomScale とオフセットによって決まります。
#import "ZoomView.h"
#import <QuartzCore/QuartzCore.h>
@implementation ZoomView
@synthesize zoomScale, offset, holdZoom;
- (id)initWithFrame:(CGRect)frame
andModel:(ZoomModel *)model
{
self = [super initWithFrame:frame];
if (self) {
m = model;
zoomScale = 1.0;
offset = CGPointMake(0, 0);
}
return self;
}
- (void)setZoomScale:(float)s
{
zoomScale *= s;
if (zoomScale < 1.0) {
zoomScale = 1.0;
}
}
- (void)setOffset:(CGPoint)o
{
//This function is to make sure we don't pan outside the content range
//it needs some work, I'm having trouble getting the panning to work
float size = m.cellSize * zoomScale;
offset = o;
if ((offset.x - self.frame.size.width/size) <= 0) {
//offset.x = self.frame.size.width;
NSLog(@"X MIN");
}
if ((offset.x + self.frame.size.width/size) >= (m.gridLength*size)) {
// offset.x = (m.gridLength*size) - self.frame.size.width;
NSLog(@"X MAX");
}
if ((offset.y - self.frame.size.height/size) <= 0) {
//offset.y = self.frame.size.height;
NSLog(@"Y MIN");
}
if ((offset.y + self.frame.size.height/size) >= (m.gridLength*size)) {
// offset.y = (m.gridHeight*size) - self.frame.size.height;
NSLog(@"Y MAX");
}
}
- (BOOL)checkCellAt:(float)x
andY:(float)y
{
int X = (int)(x/m.cellSize * zoomScale);
int Y = (int)(y/m.cellSize * zoomScale);
return [m cellAtX:X andY:Y];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor blackColor] setFill];
CGContextFillRect(ctx, rect);
float size = m.cellSize * zoomScale;
[[UIColor whiteColor] setFill];
float a = offset.x;
float b = offset.y;
//the -5 is there to give a little buffer so that half cells can be seen
// -a is taken because the offset is negative
int startX = (int)(-a/size) - 5;
int startY = (int)(-b/size) - 5;
int endX = (int)(startX) + (int)(rect.size.width/size) + 10;
int endY = (int)(startY) + (int)(rect.size.height/size) + 10;
if (startX < 0)
startX = 0;
if (startY < 0)
startY = 0;
if (endX > m.gridLength)
endX = m.gridLength;
if (endY > m.gridHeight)
endY = m.gridHeight;
[[UIColor whiteColor] setFill];
for (float i=startX; i<endX; ++i) {
for (float j=startX; j<endY; ++j) {
if ([m cellAtX:(int)i andY:(int)j]) {
//ii and jj are there to make the drawing start on the top left corner of the view
float ii = i - startX;
float jj = j - startY;
CGRect cell = CGRectMake(size*ii, size*jj, size, size);
CGContextFillRect(ctx, cell);
}
}
}
}
@end
ZoomViewController.h
このView Controllerには、ジェスチャー認識エンジンとハンドラーが含まれています
#import <Foundation/Foundation.h>
#import "ZoomModel.h"
#import "ZoomView.h"
@interface ZoomViewController : UIViewController <UIGestureRecognizerDelegate>
{
ZoomModel *m;
ZoomView *v;
}
- (void)handleZoom:(UIPinchGestureRecognizer *)recognizer;
- (void)handlePan:(UIPanGestureRecognizer *)recognizer;
@end
ZoomViewController.m
zoomView は、画面フレームをフレームとして持つ UIView 内に設定されます。zoomView 自体は、半分のセルを描画できるように、画面よりも少し大きく作られています。
#import "ZoomViewController.h"
#import <QuartzCore/QuartzCore.h>
@implementation ZoomViewController
- (void)loadView
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIView *mainView = [[UIView alloc] initWithFrame:screenRect];
float cellSize = 1;
int ni = (int)(screenRect.size.width/cellSize);
int nj = (int)(screenRect.size.height/cellSize);
CGRect zoomRect = CGRectMake(0, 0, 1.2*screenRect.size.width, 1.2*screenRect.size.height);
m = [[ZoomModel alloc] initWithLength:ni andHeight:nj andCellSize:cellSize];
v = [[ZoomView alloc] initWithFrame:zoomRect andModel:m];
v.center = CGPointMake(v.frame.size.width/2.0, v.frame.size.height/2.0);
UIPinchGestureRecognizer *zRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
action:@selector(handleZoom:)];
zRecognizer.delegate = self;
[v addGestureRecognizer:zRecognizer];
UIPanGestureRecognizer *pRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(handlePan:)];
[pRecognizer setMaximumNumberOfTouches:1];
[pRecognizer setMinimumNumberOfTouches:1];
pRecognizer.delegate = self;
[v addGestureRecognizer:pRecognizer];
[mainView addSubview:v];
[self setView:mainView];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (void)handleZoom:(UIPinchGestureRecognizer *)recognizer
{
[v setZoomScale:recognizer.scale];
//need code to zoom around the center instead of the top left corner
recognizer.scale = 1;
[v setNeedsDisplay];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
//Adjusts the offset of the view, which is used in its drawRect:
CGPoint translation = [recognizer translationInView:self.view];
CGPoint newOffset = CGPointMake(v.offset.x - translation.x, v.offset.y - translation.y);
[v setOffset:newOffset];
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
[v setNeedsDisplay];
}
@end
ZoomModel.h
このクラスは、bool のマトリックスにランダムな ON/OFF 値を入力するだけで、画面上で何かを見ることができます。アクセサー メソッドで、より複雑なアプリ モデルをシミュレートします。
#import <Foundation/Foundation.h>
@interface ZoomModel : NSObject
{
bool *grid;
}
@property (nonatomic) int gridLength;
@property (nonatomic) int gridHeight;
@property (nonatomic) float cellSize;
- (id)initWithLength:(int)l
andHeight:(int)h
andCellSize:(float)s;
- (BOOL)cellAtX:(int)x
andY:(int)y;
@end
ZoomModel.m
#import "ZoomModel.h"
@implementation ZoomModel
@synthesize gridHeight, gridLength, cellSize;
- (id)initWithLength:(int)l
andHeight:(int)h
andCellSize:(float)s
{
self = [super init];
if (self) {
grid = malloc(l*h*sizeof(bool));
gridHeight = h;
gridLength = l;
cellSize = s;
for (int i=0; i<h*l; i++) {
if (arc4random()%6 >= 4)
grid[i] = true;
else
grid[i] = false;
}
}
return self;
}
- (BOOL)cellAtX:(int)x andY:(int)y
{
return (BOOL)grid[x*gridLength + y];
}
@end