Core Animation

Пример Core Animation,

разворот пользовательских видов.

В данном примере реализован разворот пользовательских видов по оси y с помощью техники, предоставленной Core Animation.

Видео примера, с реализацией поочередной смены пользовательских видов.

Файл mbFliperViews.h

#import <Foundation/Foundation.h>

@interface mbFliperViews : NSObject
{
  NSMutableArray* views; //Animated views
  NSView* superView; // Super view
  NSInteger idxActiveView;
  NSInteger idxBefore;
  CGPoint origin; // origin for views
  NSInteger prespect; // перспектива
  double time;
}

- (void)addView:(NSView*)view;
- (void)removeViewAtIndex:(NSInteger)idx;
- (NSView*)viewAtIndex:(NSInteger)idx;
- (void)setActiveViewAtIndex:(NSInteger)idx;// Установка активного вида
- (IBAction)flipRight:(id)sender; // Переворот вправо
- (IBAction)flipLeft:(id)sender; // Переворот влево
- (IBAction)flipUp:(id)sender; // Переворот вверх
- (IBAction)flipDown:(id)sender; // Переворот вниз

@property (nonatomic) CGPoint origin; // Начальная точка координат всех видов в супервиде
@property NSInteger prespect; // перспектива, значение по умолчанию -1000
@property (retain) NSView* superView; // супервид, для вставки добавленных видов
@property double time; // время анимации в секундах, значение по умолчанию 1.0

@end

Файл mbFliperViews.m

#import "mbFliperViews.h"
#import <QuartzCore/QuartzCore.h>

@implementation mbFliperViews

@synthesize origin;
@synthesize prespect;
@synthesize superView;
@synthesize time;

- (id)init
{
  self = [super init];
  if (self) {
    views = [[NSMutableArray alloc] init];
    idxActiveView = -1;
    prespect = -1000;
    CGPoint p = {0,0};
    origin = p;
    time = 1.0;
  }
  return self;
}

// Создание слоя для вида
- (CALayer*) layerForView:(NSView*)view
{  
  [view setWantsLayer:NO];
  [view setLayer:nil];
  
  CALayer* newLayer = [CALayer layer];
  NSRect frame = view.bounds;
  //frame.origin.x = frame.origin.y = 0;
  [newLayer setBounds:frame];
  newLayer.masksToBounds = NO;
  [newLayer setContentsGravity:kCAGravityCenter];
  newLayer.doubleSided = NO;
  [view setLayer:newLayer];
  [view setWantsLayer:YES];
  return newLayer;
}

- (void)addView:(NSView*)view
{
  [self layerForView:view];
  [view setFrameOrigin:origin];
  [views addObject:view];
}

- (void)removeViewAtIndex:(NSInteger)idx
{
  [views removeObjectAtIndex:idx];
}

- (NSView*)viewAtIndex:(NSInteger)idx
{
  return [views objectAtIndex:idx];
}

- (void)setActiveViewAtIndex:(NSInteger)idx
{
  if(idx != -1){
    if(idx != idxActiveView){
      if(idxActiveView == -1)
        [superView addSubview:[self viewAtIndex:idx]];
      else 
        [superView replaceSubview:[self viewAtIndex:idxActiveView] with:[self viewAtIndex:idx]];
      [[self viewAtIndex:idx] setHidden:NO];
      idxActiveView = idx;
    }
  }
  else if(idxActiveView!=-1) {
    [[self viewAtIndex:idxActiveView] removeFromSuperview];
  }
}

-(void)setOrigin:(CGPoint)orig //setter для свойства origin
{
  origin = orig;
  NSInteger len = views.count;
  for(NSInteger i=0;i<len;i++){
    [[self viewAtIndex:i] setFrameOrigin:origin];
  }
}

- (CAAnimation*) createAnimFrom:(double)frAngl to:(double)toAngl yx:(NSInteger)yxRot
{
  NSString* sRotation;
  if (!yxRot)
    sRotation = @"transform.rotation.y";
  else 
    sRotation = @"transform.rotation.x";
  CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:sRotation];
  ba.fromValue = [NSNumber numberWithFloat:frAngl];
  ba.toValue = [NSNumber numberWithFloat:toAngl];
  CABasicAnimation *shrinkAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
  shrinkAnimation.toValue = [NSNumber numberWithFloat:0.7f];
  shrinkAnimation.duration =  time*0.5;
  shrinkAnimation.autoreverses = YES;
  
  CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
  animationGroup.animations = [NSArray arrayWithObjects:ba, shrinkAnimation, nil];
  animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  animationGroup.duration = time;
  animationGroup.fillMode = kCAFillModeForwards;
  animationGroup.removedOnCompletion = NO;
  
  return animationGroup;

}

-(void) flip:(NSInteger)direction //0 вправо, 1 влево, 2 вверх, 3 вниз
{
   NSInteger lenViews = views.count;
  if(lenViews > 1){
    NSView* currView = [self viewAtIndex:idxActiveView];
    NSInteger nextIdx;
    NSView* nextView;
    if(!(direction & 1)){ //ротация вперед
      if(idxActiveView+1 < lenViews){
        nextIdx = idxActiveView+1;
        nextView = [self viewAtIndex:nextIdx];
      }
      else {
        nextIdx = 0;
        nextView = [self viewAtIndex:0];
      }
    }
    else { // ротация назад
      if(idxActiveView > 0){
        nextIdx = idxActiveView-1;
        nextView = [self viewAtIndex:nextIdx];
      }
      else {
        nextIdx = views.count-1;
        nextView = [self viewAtIndex:nextIdx];
      }
    }
    [superView addSubview:nextView];
    
    // направление вращения
    double to;
    
    to = (direction & 1) ? -M_PI : M_PI;
    
    [CATransaction begin];
    NSInteger xyRot = direction >> 1;
    CAAnimation* anim1 = [self createAnimFrom:0.0 to:to yx:xyRot];
    CAAnimation* anim2 = [self createAnimFrom:-to to:0.0 yx:xyRot];
    [CATransaction commit];
    
    //добавляем перспективу
    CATransform3D mt = CATransform3DIdentity;
    mt.m34 = 1.0/(double)prespect;
    
    CALayer* lr = [self layerForView: currView];
    CALayer* nextlr = [self layerForView: nextView];
    lr.transform = mt;
    nextlr.transform = mt;
    
    NSPoint ap = {0.5,0.5}; // Начиная с OS X Mountain Lion ancorPoint по умолчанию в точке 0,0;
    lr.anchorPoint = ap;
    nextlr.anchorPoint = ap;
    
    // делегируем анимацию к этому классу, чтобы обработать сообщение об ее окончании
    // реализацией animationDidStop:finished:
    anim1.delegate = self;  
    
    [CATransaction begin];
    [lr addAnimation:anim1 forKey:@"flip"];
    [nextlr addAnimation:anim2 forKey:@"flip"];
    [CATransaction commit];
    idxBefore = idxActiveView;
    idxActiveView = nextIdx;
  }

}

- (IBAction)flipRight:(id)sender
{
  [self flip:0];
}

- (IBAction)flipLeft:(id)sender
{
  [self flip:1];
}

- (IBAction)flipUp:(id)sender
{
  [self flip:2];
}

- (IBAction)flipDown:(id)sender
{
  [self flip:3];
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
  if(flag){
    // обработка события окончания анимации, убираем предидущий вид из дерева супервида
    NSView* beforeView = [self viewAtIndex:idxBefore];
    [beforeView removeFromSuperview];
  }
}

@end

Использование вышеприведенного примера:

Добавляем два вышеперечисленных файла в проект, добавляем в проект framework QuartzCore.framework.

Далее просто используем вышеперечисленный код

  //mbFliperViews* fliper - задекларирован в .h файле
  fliper = [[mbFliperViews alloc] init];
  CGPoint org = {20,20}; // отступ от низа супервида
  fliper.origin = org;
  [fliper addView:view1]; // добавляем виды, которые можно переключать
  [fliper addView:view2];
  [fliper addView:view3];
  [fliper addView:view4];
  fliper.superView = _window.contentView; // в качестве супервида к примеру вид контента окна
  [fliper setActiveViewAtIndex:0];

Для переключения видов используем методы flipRight:, flipLeft:, flipUp: и flipDown:

 

См. также:

Core Animation, разворот Окон вокруг оси
 
 
homeЗаметили ошибкукарта сайта  EN
   Made on a Mac