Виджет «flip» в Core Animation / Cocoa

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

Представления с поддержкой слоя имеют свойство преобразования, но изменение, которое не делает то, что я ожидаю от него (rotation слоя вокруг оси y сворачивает его влево).

Я указал на некоторые недокументированные функции и файл .h с именем cgsprivate.h, но мне интересно, есть ли официальный способ сделать это? Это программное обеспечение должно быть отправлено, и я бы не хотел, чтобы он потерпел неудачу позже, потому что Apple ребята вытащили его в 10.6.

Кто-нибудь знает, как это сделать? Мне так странно, что в Core Animation было бы очень сложно сделать простой виджетов.

Заранее спасибо!

EDIT: Я могу выполнить это поведение с изображениями, которые находятся на слоях, но я не знаю, как получить более продвинутые элементы управления / представления / что-то на слоях. В примере карты используются изображения.

У Майка Ли есть реализация эффекта флип, для которого он выпустил некоторый пример кода . (К сожалению, это больше не доступно в Интернете, но Дрю Маккормак построил это в своей собственной реализации .) Похоже, что он захватывает слои для просмотра «фона» и «переднего плана», который использует CATransform3D для поворота два представления в анимации, а затем свопирует представления после завершения анимации.

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

Использование Core Animation, например, e.James, очерчено … Обратите внимание, что это использование сборки мусора и размещенного слоя:

#import "AnimationWindows.h" @interface AnimationFlipWindow (PrivateMethods) NSRect RectToScreen(NSRect aRect, NSView *aView); NSRect RectFromScreen(NSRect aRect, NSView *aView); NSRect RectFromViewToView(NSRect aRect, NSView *fromView, NSView *toView); @end #pragma mark - @implementation AnimationFlipWindow @synthesize flipForward = _flipForward; - (id) init { if ( self = [super init] ) { _flipForward = YES; } return self; } - (void) finalize { // Hint to GC for cleanup [[NSGarbageCollector defaultCollector] collectIfNeeded]; [super finalize]; } - (void) flip:(NSWindow *)activeWindow toBack:(NSWindow *)targetWindow { CGFloat duration = 1.0f * (activeWindow.currentEvent.modifierFlags & NSShiftKeyMask ? 10.0 : 1.0); CGFloat zDistance = 1500.0f; NSView *activeView = [activeWindow.contentView superview]; NSView *targetView = [targetWindow.contentView superview]; // Create an animation window CGFloat maxWidth = MAX(NSWidth(activeWindow.frame), NSWidth(targetWindow.frame)) + 500; CGFloat maxHeight = MAX(NSHeight(activeWindow.frame), NSHeight(targetWindow.frame)) + 500; CGRect animationFrame = CGRectMake(NSMidX(activeWindow.frame) - (maxWidth / 2), NSMidY(activeWindow.frame) - (maxHeight / 2), maxWidth, maxHeight); mAnimationWindow = [NSWindow initForAnimation:NSRectFromCGRect(animationFrame)]; // Add a touch of perspective CATransform3D transform = CATransform3DIdentity; transform.m34 = -1.0 / zDistance; [mAnimationWindow.contentView layer].sublayerTransform = transform; // Relocate target window near active window CGRect targetFrame = CGRectMake(NSMidX(activeWindow.frame) - (NSWidth(targetWindow.frame) / 2 ), NSMaxY(activeWindow.frame) - NSHeight(targetWindow.frame), NSWidth(targetWindow.frame), NSHeight(targetWindow.frame)); [targetWindow setFrame:NSRectFromCGRect(targetFrame) display:NO]; mTargetWindow = targetWindow; // New Active/Target Layers [CATransaction begin]; CALayer *activeWindowLayer = [activeView layerFromWindow]; CALayer *targetWindowLayer = [targetView layerFromWindow]; [CATransaction commit]; activeWindowLayer.frame = NSRectToCGRect(RectFromViewToView(activeView.frame, activeView, [mAnimationWindow contentView])); targetWindowLayer.frame = NSRectToCGRect(RectFromViewToView(targetView.frame, targetView, [mAnimationWindow contentView])); [CATransaction begin]; [[mAnimationWindow.contentView layer] addSublayer:activeWindowLayer]; [CATransaction commit]; [mAnimationWindow orderFront:nil]; [CATransaction begin]; [[mAnimationWindow.contentView layer] addSublayer:targetWindowLayer]; [CATransaction commit]; // Animate our new layers [CATransaction begin]; CAAnimation *activeAnim = [CAAnimation animationWithDuration:(duration * 0.5) flip:YES forward:_flipForward]; CAAnimation *targetAnim = [CAAnimation animationWithDuration:(duration * 0.5) flip:NO forward:_flipForward]; [CATransaction commit]; targetAnim.delegate = self; [activeWindow orderOut:nil]; [CATransaction begin]; [activeWindowLayer addAnimation:activeAnim forKey:@"flip"]; [targetWindowLayer addAnimation:targetAnim forKey:@"flip"]; [CATransaction commit]; } - (void) animationDidStop:(CAAnimation *)animation finished:(BOOL)flag { if (flag) { [mTargetWindow makeKeyAndOrderFront:nil]; [mAnimationWindow orderOut:nil]; mTargetWindow = nil; mAnimationWindow = nil; } } #pragma mark PrivateMethods: NSRect RectToScreen(NSRect aRect, NSView *aView) { aRect = [aView convertRect:aRect toView:nil]; aRect.origin = [aView.window convertBaseToScreen:aRect.origin]; return aRect; } NSRect RectFromScreen(NSRect aRect, NSView *aView) { aRect.origin = [aView.window convertScreenToBase:aRect.origin]; aRect = [aView convertRect:aRect fromView:nil]; return aRect; } NSRect RectFromViewToView(NSRect aRect, NSView *fromView, NSView *toView) { aRect = RectToScreen(aRect, fromView); aRect = RectFromScreen(aRect, toView); return aRect; } @end #pragma mark - #pragma mark CategoryMethods: @implementation CAAnimation (AnimationFlipWindow) + (CAAnimation *) animationWithDuration:(CGFloat)time flip:(BOOL)bFlip forward:(BOOL)forwardFlip{ CABasicAnimation *flipAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; CGFloat startValue, endValue; if ( forwardFlip ) { startValue = bFlip ? 0.0f : -M_PI; endValue = bFlip ? M_PI : 0.0f; } else { startValue = bFlip ? 0.0f : M_PI; endValue = bFlip ? -M_PI : 0.0f; } flipAnimation.fromValue = [NSNumber numberWithDouble:startValue]; flipAnimation.toValue = [NSNumber numberWithDouble:endValue]; CABasicAnimation *shrinkAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; shrinkAnimation.toValue = [NSNumber numberWithFloat:1.3f]; shrinkAnimation.duration = time * 0.5; shrinkAnimation.autoreverses = YES; CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.animations = [NSArray arrayWithObjects:flipAnimation, shrinkAnimation, nil]; animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; animationGroup.duration = time; animationGroup.fillMode = kCAFillModeForwards; animationGroup.removedOnCompletion = NO; return animationGroup; } @end #pragma mark - @implementation NSWindow (AnimationFlipWindow) + (NSWindow *) initForAnimation:(NSRect)aFrame { NSWindow *window = [[NSWindow alloc] initWithContentRect:aFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; [window setOpaque:NO]; [window setHasShadow:NO]; [window setBackgroundColor:[NSColor clearColor]]; [window.contentView setWantsLayer:YES]; return window; } @end #pragma mark - @implementation NSView (AnimationFlipWindow) - (CALayer *) layerFromWindow { NSBitmapImageRep *image = [self bitmapImageRepForCachingDisplayInRect:self.bounds]; [self cacheDisplayInRect:self.bounds toBitmapImageRep:image]; CALayer *layer = [CALayer layer]; layer.contents = (id)image.CGImage; layer.doubleSided = NO; // Shadow settings based upon Mac OS X 10.6 [layer setShadowOpacity:0.5f]; [layer setShadowOffset:CGSizeMake(0,-10)]; [layer setShadowRadius:15.0f]; return layer; } @end 

Файл заголовка:

 @interface AnimationFlipWindow : NSObject { BOOL _flipForward; NSWindow *mAnimationWindow; NSWindow *mTargetWindow; } // Direction of flip animation (property) @property (readwrite, getter=isFlipForward) BOOL flipForward; - (void) flip:(NSWindow *)activeWindow toBack:(NSWindow *)targetWindow; @end #pragma mark - #pragma mark CategoryMethods: @interface CAAnimation (AnimationFlipWindow) + (CAAnimation *) animationWithDuration:(CGFloat)time flip:(BOOL)bFlip // Flip for each side forward:(BOOL)forwardFlip; // Direction of flip @end @interface NSWindow (AnimationFlipWindow) + (NSWindow *) initForAnimation:(NSRect)aFrame; @end @interface NSView (AnimationFlipWindow) - (CALayer *) layerFromWindow; @end 

EDIT: это будет оживлять, чтобы перевернуть из одного windows в другое окно. Вы можете применить те же принципы к виду.

Это слишком много для ваших целей (так как в нем содержится полностью заполненное приложение для справки по карточной игре и карточной игре), но проверьте этот образец от АЦП . Карточные игры, включенные в него, делают этот эффект флип довольно приятным.

Если вы можете сделать это с помощью изображений, возможно, вы можете сохранить все свои элементы управления в объекте NSView (как обычно), а затем визуализировать NSView в bitmap, используя cacheDisplayInRect:toBitmapImageRep: непосредственно перед выполнением эффекта cacheDisplayInRect:toBitmapImageRep: . Этапы:

  1. NSView в bitmap
  2. Отображение этого растрового изображения в слое, подходящем для эффекта переворота
  3. Скрыть NSView и выставить слой изображения
  4. Выполнить эффект переворота

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

https://developer.apple.com/library/mac/#samplecode/ImageTransition/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010277

В Mizage есть полная версия с открытым исходным кодом.

Вы можете проверить это здесь: https://github.com/mizage/Flip-Animation

Вероятно, это не так в 2008 году, когда задавали этот вопрос, но в наши дни это довольно легко:

 [UIView animateWithDuration:0.5 animations:^{ [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.iconView cache:YES]; /* changes to the view made here will be reflected on the flipped to side */ }]; 

Примечание. По-видимому, это работает только на iOS.

Давайте будем гением компьютера.