(iOS) Использование UIGestureRecognizer для масштабирования и панорамирования вида

Я пытаюсь выполнить масштабирование и панорамирование аналогично UIScrollView с помощью UIGestures.

Мое представление основано на матрице ячеек ВКЛ / ВЫКЛ и должно поддерживать тысячи ячеек. Метод drawRect: заботится о переводе координат матрицы в экранные координаты. Представление имеет свойство для величины масштабирования и CGPoint, который содержит смещение.

Я думаю, что если я смогу разобраться с масштабированием и панорамированием следующего, я должен быть хорош. Извините за стену кода ниже, но она представляет собой полную реализацию, которая отражает мою более сложную программу.

Прямо сейчас масштабирование все масштабирует, но ему нужен способ центрироваться, как это делает масштабирование UIScrollView.

Панорамирование просто не работает вообще.


ZoomView.h

ZoomView заботится о рисовании матрицы bools.

#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: выполняет вычисления для определения того, какой элемент матрицы должен находиться в видимой части экрана. Видимая часть экрана определяется масштабом и смещением.

#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

Этот контроллер представления содержит распознаватели жестов и обработчики

#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

Этот класс просто заполняет матрицу bools случайными значениями 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

0 ответов

Другие вопросы по тегам