ПРОГРАММИРОВАНИЕ ПОД IPHONE, IPAD OBJECTIVE-C часть 3

21. 3D

Псевдо 3D в iOS

В этом уроке я хочу поведать об интересном способе отображения 3D объектов, которые на самом деле таковыми не являются. Почему я предложил именно такой вариант? Дело в том, что создавать и отображать реальные 3D объекты довольно сложная задача для программиста. И этот урок будет как бы переходящей ступенью к миру OpenGL. И так, у нас есть 36 картинок для эмуляции псевдо 3D, это как раз 36 кадров полного поворота. При старте слудет загружать эти изображения, по умолчанию отображать первое из них. Ставим палец на объект и не отпуская тянем его — модель вращается за пальцем до тех пор, пока тянем. Если останавливаем палец — вращение останавливается, если тянем в другую сторону — вращается за пальцем в другую сторону с той скоростью с которой тянем палец. Если взмахиваем по объекту — он начинает вращаться в ту сторону, в которую был произведен взмах. Вращение происходит с постоянно скоростью (для облегчения задачи) до тех пор, пока не остановим его одним касанием.

 

Приступим к реализации поставленой задачи. Создадим новый проект на основе шаблона Single View Application, назовем его Pseudo3D и добавим оговореный выше набор картинок, который можна сказать здесь. Теперь определимся с переменными которые нам необходимы и что мы будем в них хранить. Первое, что нам нужно — это чувствительность вращения объекта от движения пальца и скорость вращения после взмахивания. Их мы будем хранить в дефайнах. Затем нам понадобится группа с трех переменных (свойства для которых прописывать не нужно), а именно:

 

  • индекс текущей картинки
  • направление прокручивания
  • коорднаты последннего касания

 

И еще три, для которых нужны методы доступа:

 

  • UIImageView, в котором будут отображаться картинки
  • массив загруженых в память картинок
  • таймер для анимированого просмотра

 

Все эти переменные описаны в интерфейсе класса ViewController в том соответственном порядке:

 

<code data-result="[object Object]">#define sensitivityRotation 8
#define animationDuration 0.05f

typedef enum {
    Right = -1,
    Left = 1,
} SenseRotation;

@interface ViewController : UIViewController {
    NSInteger imageIndex;
    SenseRotation senseRotation;
    CGPoint oldTouchLocation;
}

@property (strong, nonatomic) IBOutlet UIImageView *earth;
@property (strong, nonatomic) NSMutableArray *animationImages;
@property (strong, nonatomic) NSTimer *animationTimer;

@end</code>

 

Прежде чем перейти к коду реализации ViewController предлагаю определиться с его методами:

 

  • чтение и загрузка изображений в массив
  • добавление обработчиков касаний и жестов
  • вращение объекта
  • установка текущей картинки
  • зацикливание картинок

 

А вот как это выглядит в коде:

 

<code data-result="[object Object]">#import "ViewController.h"

@interface ViewController ()
- (void)loadImageList;
- (void)addGestures;
- (void)rotateEarth;
- (void)setCurrentImage;
- (void)loopImageIndex;
@end

@implementation ViewController

@synthesize earth;
@synthesize animationImages;
@synthesize animationTimer;

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.earth = nil;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self loadImageList];
    [self addGestures];

    imageIndex = 0;
    [self setCurrentImage];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [animationTimer invalidate];
}

- (void)loadImageList {
    self.animationImages = [NSMutableArray array];
    for (int i=1; i&lt;=36; i++) {
        NSString *fileName = [NSString stringWithFormat:@"%i.png", i];
        [animationImages addObject:[UIImage imageNamed:fileName]];
    }
}

- (void)addGestures {
    UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self 
                                                                                    action:@selector(didSwipe:)];
    swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
    [earth addGestureRecognizer:swipeLeft];

    UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self 
                                                                                     action:@selector(didSwipe:)];
    swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
    [earth addGestureRecognizer:swipeRight];

    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self 
                                                                                            action:@selector(handleLongPress:)];
    longPress.minimumPressDuration = 0.05f;
    [earth addGestureRecognizer:longPress];
}

- (void)didSwipe:(UISwipeGestureRecognizer*)swipe {
    if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
        senseRotation = Left;
    } else if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
        senseRotation = Right;
    }

    [animationTimer invalidate];
    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationDuration
                                                           target:self 
                                                         selector:@selector(rotateEarth) 
                                                         userInfo:nil 
                                                          repeats:YES];
}

- (void)handleLongPress:(UILongPressGestureRecognizer*)recognizer {
    [animationTimer invalidate];

    if (recognizer.state == UIGestureRecognizerStateChanged) {
        CGPoint touchLocation = [recognizer locationInView:earth];
        senseRotation = touchLocation.x&lt;oldTouchLocation.x? Left:Right;

        if (abs(touchLocation.x - oldTouchLocation.x) &gt; sensitivityRotation) {
            [self rotateEarth];
            oldTouchLocation = touchLocation;
        }
    }
}

- (void)rotateEarth {
    imageIndex = imageIndex + senseRotation;
    [self loopImageIndex];
    [self setCurrentImage];
}

- (void)setCurrentImage {
    earth.image = [animationImages objectAtIndex:imageIndex];
}

- (void)loopImageIndex {
    if (imageIndex == animationImages.count) {
        imageIndex = 0;
    } else if (imageIndex &lt; 0) {
        imageIndex = animationImages.count-1;
    }
}

@end</code>

 

Отдельное внимане хочу уделить UIGestureRecognizer, я уже рассказывал о его сабклассах и их совместного перекрывания. Это свойство в данном случае сработало нам «на руку». Дело в том, что я намеренно установил minimumPressDurationдля UILongPressGestureRecognizer равным 0.05 секунды. То есть, если человек хочет применить жест взмахивания для того, чтобы объект начал крутиться — длительность касания к объекту будет меньше указаного выше времени и сработает UISwipeGestureRecognizer, в противном случае обработчик UILongPressGestureRecognizer перекроет действие распознавателя взмахов и будет выполнять обработку протягивания пальца по экрану. Объяснять работу остальных методов считаю не целесообразным, поскольку краткое описание их назначения уже было изложено выше. Главное — не забудье добавить UIImageView на интерфейс и связать его с объектом earth, а так же поставить галочку в его поле User Interaction Enabled.

Comments are closed.