I'm trying to make a Card class that duplicates the behavior of Dashboard widgets in that you can put controls or images or whatever on two sides of the card and flip between them.
Layer backed views have a transform property, but altering that doesn't do what I would expect it to do (rotating the layer around the y axis folds it off to the left side).
I was pointed to some undocumented features and an .h file named cgsprivate.h, but I'm wondering if there is an official way to do this? This software would have to be shipped and I'd hate to see it fail later because the Apple guys pull it in 10.6.
Anyone have any idea how to do this? It's so weird to me that a simple widget thing would be so hard to do in Core Animation.
Thanks in advance!
EDIT: I can accomplish this behavior with images that are on layers, but I don't know how to get more advanced controls/views/whatever on the layers. The card example uses images.
Mike Lee has an implementation of the flip effect for which he has released some sample code. (Unfortunately, this is no longer available online, but Drew McCormack built off of that in his own implementation.) It appears that he grabs the layers for the "background" and "foreground" views to be swapped, uses a CATransform3D to rotate the two views in the animation, and then swaps the views once the animation has completed.
By using the layers from the views, you avoid needing to cache into a bitmap, since that's what the layers are doing anyways. In any case, his view controller looks to be a good drop-in solution for what you want.
Using Core Animation like e.James outlined...Note, this is using garbage collection and a hosted layer:
#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
The header file:
#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: This will animate to flip from one window to another window. You can apply the same principals to a view.
It's overkill for your purposes (as it contains a largely-complete board and card game reference app), but check out this sample from ADC. The card games included with it do that flip effect quite nicely.
If you are able to do this with images, perhaps you can keep all of your controls in an NSView object (as usual), and then render the NSView into a bitmap image using cacheDisplayInRect:toBitmapImageRep: just prior to executing the flip effect. The steps would be:
Render the NSView to a bitmap
Display that bitmap in a layer suitable for the flip effect
Hide the NSView and expose the image layer
Perform the flip effect
I know this is late but Apple has an example project here that may be of help to anyone still stumbling upon this question.
https://developer.apple.com/library/mac/#samplecode/ImageTransition/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010277
There's a complete open source implementation of this by the guys at Mizage.
You can check it out here: https://github.com/mizage/Flip-Animation
Probably not the case in 2008 when this question was asked, but this is pretty easy these days:
[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 */
}];
Note: Apparently, this only works on iOS.
Related
How to make flip animation for OS X application windows without complex coding?
Finally, I did it. I have created object that work with NSWindowController objects instead of NSWidows.
ALWindowFlipAnimator.h
#import <Foundation/Foundation.h>
//............................................................................................................
// Shorten macroes:
#define FLIPANIMATOR [ALWindowFlipAnimator sharedWindowFlipAnimator]
//............................................................................................................
// Window flip direction:
typedef NS_ENUM(NSUInteger, ALFlipDirection)
{
ALFlipDirectionLeft,
ALFlipDirectionRight,
ALFlipDirectionUp,
ALFlipDirectionDown
};
#interface ALWindowFlipAnimator : NSObject
+(ALWindowFlipAnimator *)sharedWindowFlipAnimator;
-(void)flipToWindowNibName:(NSString *)nibName direction:(ALFlipDirection)direction;
#end
ALWindowFlipAnimator.m
#import "ALWindowFlipAnimator.h"
#import <QuartzCore/QuartzCore.h>
#implementation ALWindowFlipAnimator
{
NSWindowController *_currentWindowController; // Current window controller
NSWindowController *_nextWindowController; // Next window controller to flip to
NSWindow *_animationWindow; // Window where flip animation plays
}
//============================================================================================================
// Initialize flip window controller
//============================================================================================================
-(id)init
{
self = [super init];
if (self)
{
_currentWindowController = nil;
_nextWindowController = nil;
_animationWindow = nil;
}
return self;
}
//============================================================================================================
// Create shared flip window manager
//============================================================================================================
+(ALWindowFlipAnimator *)sharedWindowFlipAnimator
{
static ALWindowFlipAnimator *wfa = nil;
if (!wfa) wfa = [[ALWindowFlipAnimator alloc] init];
return wfa;
}
//============================================================================================================
// Flip to window with selected NIB file name from the current one
//============================================================================================================
-(void)flipToWindowNibName:(NSString *)nibName direction:(ALFlipDirection)direction
{
if (!_currentWindowController || ![[_currentWindowController window] isVisible])
{
// No current window controller or window is closed
_currentWindowController = [[NSClassFromString(nibName) alloc] initWithWindowNibName:nibName];
[_currentWindowController showWindow:self];
}
else
{
if ([[_currentWindowController className] isEqualToString:nibName])
// Bring current window to front
[[_currentWindowController window] makeKeyAndOrderFront:self];
else
{
// Flip to new window
_nextWindowController = [[NSClassFromString(nibName) alloc] initWithWindowNibName:nibName];
[self flipToNextWindowControllerDirection:direction];
}
}
}
#pragma mark - Flip animation
#define DEF_DURATION 2.0 // Animation duration
#define DEF_SCALE 1.2 // Scaling factor for animation window (_animationWindow)
//============================================================================================================
// Start window flipping animation
//============================================================================================================
-(void)flipToNextWindowControllerDirection:(ALFlipDirection)direction
{
NSWindow *currentWindow = [_currentWindowController window];
NSWindow *nextWindow = [_nextWindowController window];
NSView *currentWindowView = [currentWindow.contentView superview];
NSView *nextWindowView = [nextWindow.contentView superview];
// Create window for animation
CGFloat maxWidth = MAX(currentWindow.frame.size.width, nextWindow.frame.size.width);
CGFloat maxHeight = MAX(currentWindow.frame.size.height, nextWindow.frame.size.height);
CGFloat xscale = DEF_SCALE * 2.0;
maxWidth += maxWidth * xscale;
maxHeight += maxHeight * xscale;
CGRect animationFrame = CGRectMake(NSMidX(currentWindow.frame) - (maxWidth / 2),
NSMidY(currentWindow.frame) - (maxHeight / 2),
maxWidth, maxHeight);
_animationWindow = [[NSWindow alloc] initWithContentRect:NSRectFromCGRect(animationFrame)
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO];
[_animationWindow setOpaque:NO];
[_animationWindow setHasShadow:NO];
[_animationWindow setBackgroundColor:[NSColor clearColor]];
[_animationWindow.contentView setWantsLayer:YES];
[_animationWindow setLevel:NSScreenSaverWindowLevel];
// Move next window closer to the current one
CGRect nextFrame = CGRectMake(NSMidX(currentWindow.frame) - (NSWidth(nextWindow.frame) / 2 ),
NSMaxY(currentWindow.frame) - NSHeight(nextWindow.frame),
NSWidth(nextWindow.frame), NSHeight(nextWindow.frame));
[nextWindow setFrame:NSRectFromCGRect(nextFrame) display:NO];
// Make snapshots of current and next windows
[CATransaction begin];
CALayer *currentWindowSnapshot = [self snapshotToImageLayerFromView:currentWindowView];
CALayer *nextWindowSnapshot = [self snapshotToImageLayerFromView:nextWindowView];
[CATransaction commit];
currentWindowSnapshot.frame = [self rect:currentWindowView.frame
fromView:currentWindowView
toView:[_animationWindow contentView]];
nextWindowSnapshot.frame = [self rect:nextWindowView.frame
fromView:nextWindowView
toView:[_animationWindow contentView]];
// Create 3D transform matrix to snapshots
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -(1.0 / 1500.0);
currentWindowSnapshot.transform = transform;
nextWindowSnapshot.transform = transform;
// Add snapshots to animation window
[CATransaction begin];
[[_animationWindow.contentView layer] addSublayer:currentWindowSnapshot];
[[_animationWindow.contentView layer] addSublayer:nextWindowSnapshot];
[CATransaction commit];
[_animationWindow makeKeyAndOrderFront:nil];
// Animation for snapshots
[CATransaction begin];
CAAnimation *currentSnapshotAnimation = [self animationWithDuration:(DEF_DURATION * 0.5) flip:YES direction:direction];
CAAnimation *nextSnapshotAnimation = [self animationWithDuration:(DEF_DURATION * 0.5) flip:NO direction:direction];
[CATransaction commit];
// Start animation
nextSnapshotAnimation.delegate = self;
[currentWindow orderOut:nil];
[CATransaction begin];
[currentWindowSnapshot addAnimation:currentSnapshotAnimation forKey:#"flipAnimation"];
[nextWindowSnapshot addAnimation:nextSnapshotAnimation forKey:#"flipAnimation"];
[CATransaction commit];
}
//============================================================================================================
// Convert rectangle from one view coordinates to another
//============================================================================================================
-(CGRect)rect:(NSRect)rect fromView:(NSView *)fromView toView:(NSView *)toView
{
rect = [fromView convertRect:rect toView:nil];
rect = [fromView.window convertRectToScreen:rect];
rect = [toView.window convertRectFromScreen:rect];
rect = [toView convertRect:rect fromView:nil];
return NSRectToCGRect(rect);
}
//============================================================================================================
// Get snapshot of selected view as layer with bitmap image
//============================================================================================================
-(CALayer *)snapshotToImageLayerFromView:(NSView*)view
{
// Make view snapshot
NSBitmapImageRep *snapshot = [view bitmapImageRepForCachingDisplayInRect:view.bounds];
[view cacheDisplayInRect:view.bounds toBitmapImageRep:snapshot];
// Convert snapshot to layer
CALayer *layer = [CALayer layer];
layer.contents = (id)snapshot.CGImage;
layer.doubleSided = NO;
// Add shadow of window to snapshot
[layer setShadowOpacity:0.5];
[layer setShadowOffset:CGSizeMake(0.0, -10.0)];
[layer setShadowRadius:15.0];
return layer;
}
//============================================================================================================
// Create animation
//============================================================================================================
-(CAAnimation *)animationWithDuration:(CGFloat)time flip:(BOOL)flip direction:(ALFlipDirection)direction
{
// Set flip direction
NSString *keyPath = #"transform.rotation.y";
if (direction == ALFlipDirectionUp || direction == ALFlipDirectionDown) keyPath = #"transform.rotation.x";
CABasicAnimation *flipAnimation = [CABasicAnimation animationWithKeyPath:keyPath];
CGFloat startValue = flip ? 0.0 : -M_PI;
CGFloat endValue = flip ? M_PI : 0.0;
if (direction == ALFlipDirectionLeft || direction == ALFlipDirectionUp)
{
startValue = flip ? 0.0 : M_PI;
endValue = flip ? -M_PI : 0.0;
}
flipAnimation.fromValue = [NSNumber numberWithDouble:startValue];
flipAnimation.toValue = [NSNumber numberWithDouble:endValue];
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.toValue = [NSNumber numberWithFloat:DEF_SCALE];
scaleAnimation.duration = time * 0.5;
scaleAnimation.autoreverses = YES;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = [NSArray arrayWithObjects:flipAnimation, scaleAnimation, nil];
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animationGroup.duration = time;
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.removedOnCompletion = NO;
return animationGroup;
}
//============================================================================================================
// Flip animation did finish
//============================================================================================================
-(void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
{
[[_nextWindowController window] makeKeyAndOrderFront:nil];
[_animationWindow orderOut:nil];
_animationWindow = nil;
_currentWindowController = _nextWindowController;
}
#end
How to use:
Create some new NSWindowController objects with NIBs in your project
Call [FLIPANIMATOR flipToWindowNibName:#"SecondWindowController" direction:ALFlipDirectionRight]; to flip to second window, or [FLIPANIMATOR flipToWindowNibName:#"FirstWindowController" direction:ALFlipDirectionLeft]; to return back
Link QuarzCore.framework to your project
That's all!
i like to subclass a NSProgressIndicator. I've used this code and i set the Subclass in the Interface Builder:
- (void)drawRect:(NSRect)dirtyRect {
NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
CGFloat radius = rect.size.height / 2;
NSBezierPath *bz = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
[bz setLineWidth:2.0];
[[NSColor blackColor] set];
[bz stroke];
rect = NSInsetRect(rect, 2.0, 2.0);
radius = rect.size.height / 2;
bz = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
[bz setLineWidth:1.0];
[bz addClip];
rect.size.width = floor(rect.size.width * ([self doubleValue] / [self maxValue]));
NSRectFill(rect);
When the app starts its looks like this:
But during the copy progress the old bar shows up.
Whats wrong?
It seems that the progress bar's progress is not drawn in drawRect:, so just overriding drawRect: is not enough. However, if you make the progress bar layer backed, you are responsible for doing all the drawing.
From the documentation:
The view class automatically creates a backing layer for you (using
makeBackingLayer if overridden), and you must use the view class’s
drawing mechanisms.
Check the "Core Animation Layer" in IB or add this to your sub class:
- (void)awakeFromNib {
[super awakeFromNib];
[self setWantsLayer:YES];
}
I followed the above advice (thanks!), but unfortunately discovered that when the NSProgressIndicator was resized, it disappeared, but only on the first viewing (it was inside a drawer).
Rather than try and understand what was happening, I realised you don't actually need the old control, as what I was doing is very simple (a strength indicator that changes color). Just create a subclass of NSView.
So this is it:
#interface SSStrengthIndicator : NSView
/// Set the indicator based upon a score from 0..4
#property (nonatomic) double strengthScore;
#end
#implementation SSStrengthIndicator
- (void)setStrengthScore:(double)strength
{
if (_strengthScore != strength) {
_strengthScore = strength;
[self setNeedsDisplay:YES];
}
}
- (void)drawRect:(NSRect)dirtyRect
{
NSRect rect = NSInsetRect([self bounds], 1.0, 2.0);
double val = (_strengthScore + 1) * 20;
if (val <= 40)
[[NSColor redColor] set];
else if (val <= 60)
[[NSColor yellowColor] set];
else
[[NSColor greenColor] set];
rect.size.width = floor(rect.size.width * (val / 100.0));
[NSBezierPath fillRect:rect];
}
#end
My requirement is to display the list of images in horizantal way.when I touch the image it is moved to side and the next image is comes to front.In this way I need to move images side by side.Then is there any method to scroll the list of images?or Can I go for any type of animation ?
In this way images are to be placed and moved.Please suggest me how can I proceed?
I have created a working module of cover flow.....
try transformation on layers which allows 3D transformation using....
-(void) rotateView3D:(UIView *)myView:(CGFloat)angle
{
CATransform3D affinAndScaleTransform;
if (angle==30.0f||angle==-30.0f)
{
affinAndScaleTransform = CATransform3DMakeAffineTransform(CGAffineTransformMakeScale(0.9f, 0.9f));
}
else
{
affinAndScaleTransform = CATransform3DMakeAffineTransform(CGAffineTransformMakeScale(1.0f, 1.0f));
}
CALayer *layer = myView.layer;
// layer.borderColor = [[UIColor blackColor] CGColor];
// layer.borderWidth = 2.0f;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, angle * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
[layer setTransform:CATransform3DConcat(rotationAndPerspectiveTransform, affinAndScaleTransform)];
}
to scroll the views add gesture .......
for left gesture use some thig like this ........
{
UIView * view0 = [self.view viewWithTag:-1111];
UIView * view1 = [self.view viewWithTag:1111];
UIView * view2 = [self.view viewWithTag:2222];
UIView * view3 = [self.view viewWithTag:3333];
UIView * view4 = [self.view viewWithTag:4444];
[view4 setFrame:self.rect0];
int preViewNo = self.leftProduct-1;
if (preViewNo<0)preViewNo=self.productArray.count-1;
[UIView animateWithDuration:0.4 animations:^
{
[view0 setFrame:self.rect1];
[self rotateView3D:view0 : 30.0f];
[view1 setFrame:self.rect2];
[self rotateView3D:view1 : 0.0f];
[view2 setFrame:self.rect3];
[self rotateView3D:view2 : -30.0f];
[view3 setFrame:self.rect4];
[self rotateView3D:view4 : 0.0f];
}];
[self setdescriptionView:view0 :[self.productArray objectAtIndex:preViewNo]];
[view0 setTag:1111];
[view1 setTag:2222];
[view2 setTag:3333];
[view3 setTag:4444];
[view4 setTag:-1111];
self.currentProduct--;
self.rightProduct--;
self.leftProduct--;
if (self.currentProduct < 0) self.currentProduct=self.productArray.count-1;
if (self.rightProduct < 0) self.rightProduct=self.productArray.count-1;
if (self.leftProduct < 0) self.leftProduct=self.productArray.count-1;
}
on right gesture recognizer use some thing like this.......
{
UIView * view0 = [self.view viewWithTag:-1111];
UIView * view1 = [self.view viewWithTag:1111];
UIView * view2 = [self.view viewWithTag:2222];
UIView * view3 = [self.view viewWithTag:3333];
UIView * view4 = [self.view viewWithTag:4444];
[view0 setFrame:self.rect4];
int nextViewNo = self.rightProduct+1;
if (nextViewNo>=self.productArray.count)nextViewNo=0;
[UIView animateWithDuration:0.4 animations:^
{
[view4 setFrame:self.rect3];
[self rotateView3D:view4 : -30.0f];
[view3 setFrame:self.rect2];
[self rotateView3D:view3 : 0.0f];
[view2 setFrame:self.rect1];
[self rotateView3D:view2 : 30.0f];
[view1 setFrame:self.rect0];
[self rotateView3D:view1 : 0.0f];
}];
[self setdescriptionView:view4 :[self.productArray objectAtIndex:nextViewNo]];
[view0 setTag:4444];
[view1 setTag:-1111];
[view2 setTag:1111];
[view3 setTag:2222];
[view4 setTag:3333];
self.currentProduct++;
self.rightProduct++;
self.leftProduct++;
if (self.currentProduct >= self.productArray.count) self.currentProduct=0;
if (self.rightProduct >= self.productArray.count) self.rightProduct=0;
if (self.leftProduct >= self.productArray.count) self.leftProduct=0;
}
for some technical reason i have to make custom coverflow....
you can also try this .....
icarousal
I solve this problem .I found the sample example in this link.http://code4app.net/category/scrollview
I need to assign a view to an NSMenuItem and do some custom drawing. Basically, I'm adding a little delete button next to the currently selected menu item, among other things. But I want my custom menu item to look and behave like a regular menu item in all other ways. According to the doc:
A menu item with a view does not draw
its title, state, font, or other
standard drawing attributes, and
assigns drawing responsibility
entirely to the view.
Ok, so I had to duplicate the look of the state column and the selection gradient, which wasn't that hard. The part I'm having trouble with is the way the menu item "flashes" or "blinks" after it is selected. I'm using an NSTimer to try to mimic this little animation, but it just feels off. How many times does it blink? What time interval should I use? I've experimented a lot and it just feels out of whack.
Has anyone done this before or have other suggestions on how to add a button to a menu item? Maybe there should be a stack exchange site just for custom cocoa drawing...
I know this is over a year old, but this was the first hit on my Google search and was unanswered, so I'm posting my answer for sake of those still looking for a solution.
For my app, I used Core Animation with a custom NSView for the NSMenuItem view. I created a new layer-backed view, set the background color, and added it to my custom view. I then animated the layer (the flashing part). Then in the -(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag callback, I removed the overlay and closed the menu. This doesn't perfectly match the default NSMenu's flash, but I wanted a 37Signals/Stack Overflow Yellow Fade Technique, so it works for me. Here it is in code:
-(void) mouseUp:(NSEvent *)theEvent {
CALayer *layer = [CALayer layer];
[layer setDelegate:self];
[layer setBackgroundColor:CGColorCreateGenericRGB(0.0, 0.0, 1.0, 1.0)];
selectionOverlayView = [[NSView alloc] init];
[selectionOverlayView setWantsLayer:YES];
[selectionOverlayView setFrame:self.frame];
[selectionOverlayView setLayer:layer];
[[selectionOverlayView layer] setNeedsDisplay];
[selectionOverlayView setAlphaValue:0.0];
[self addSubview:selectionOverlayView];
CABasicAnimation *alphaAnimation1 = [CABasicAnimation animationWithKeyPath: #"alphaValue"];
alphaAnimation1.beginTime = 0.0;
alphaAnimation1.fromValue = [NSNumber numberWithFloat: 0.0];
alphaAnimation1.toValue = [NSNumber numberWithFloat: 1.0];
alphaAnimation1.duration = 0.07;
CABasicAnimation *alphaAnimation2 = [CABasicAnimation animationWithKeyPath: #"alphaValue"];
alphaAnimation2.beginTime = 0.07;
alphaAnimation2.fromValue = [NSNumber numberWithFloat: 1.0];
alphaAnimation2.toValue = [NSNumber numberWithFloat: 0.0];
alphaAnimation2.duration = 0.07;
CAAnimationGroup *selectionAnimation = [CAAnimationGroup animation];
selectionAnimation.delegate = self;
selectionAnimation.animations = [NSArray arrayWithObjects:alphaAnimation1, alphaAnimation2, nil];
selectionAnimation.duration = 0.14;
[selectionOverlayView setAnimations:[NSDictionary dictionaryWithObject:selectionAnimation forKey:#"frameOrigin"]];
[[selectionOverlayView animator] setFrame:[selectionOverlayView frame]];
}
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
[selectionOverlayView removeFromSuperview];
NSMenuItem *enclosingMenuItem = [self enclosingMenuItem];
NSMenu *enclosingMenu = [enclosingMenuItem menu];
[enclosingMenu cancelTracking];
[enclosingMenu performActionForItemAtIndex:[enclosingMenu indexOfItem:enclosingMenuItem]];
}
It is actually possible to have your custom view flash like a regular NSMenuItem without implementing the animation manually.
Note: this uses a private API and also fixes a handful of other strange NSMenuItem quirks related to custom views.
NSMenuItem.h
#import <AppKit/AppKit.h>
#interface NSMenuItem ()
- (BOOL)_viewHandlesEvents;
#end
Bridging Header
#import "NSMenuItem.h"
MenuItem.swift
class MenuItem: NSMenuItem {
override func _viewHandlesEvents() -> Bool {
return false
}
}
This API really ought to be public, and if you're not developing for the App Store, it might be worth having a look at.
Here is my code that flashes a custom menu item.
int16_t fireTimes;
BOOL isSelected;
- (void)mouseEntered:(NSEvent*)event
{
isSelected = YES;
}
- (void)mouseUp:(NSEvent*)event {
fireTimes = 0;
isSelected = !isSelected;
[self setNeedsDisplay:YES];
NSTimer *timer = [NSTimer timerWithTimeInterval:0.05 target:self selector:#selector(animateDismiss:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
}
-(void)animateDismiss:(NSTimer *)aTimer
{
if (fireTimes <= 2) {
isSelected = !isSelected;
[self setNeedsDisplay:YES];
} else {
[aTimer invalidate];
[self sendAction];
}
fireTimes++;
}
- (void)drawRect:(NSRect)dirtyRect {
if (isSelected) {
NSRect frame = NSInsetRect([self frame], -4.0f, -4.0f);
[[NSColor selectedMenuItemColor] set];
NSRectFill(frame);
[itemNameFld setTextColor:[NSColor whiteColor]];
} else {
[itemNameFld setTextColor:[NSColor blackColor]];
}
}
- (void)sendAction
{
NSMenuItem *actualMenuItem = [self enclosingMenuItem];
[NSApp sendAction:[actualMenuItem action] to:[actualMenuItem target] from:actualMenuItem];
NSMenu *menu = [actualMenuItem menu];
[menu cancelTracking];
// [self setNeedsDisplay:YES]; // I'm not sure of this
}
I'm writting a Cocoa app. I've wrote a code that can Drag&Drop one Image. But now I need to draw several Images by double click and D&D them. Each double click- new image, each click on existing image- starts D&D. Problem is in realization. I can't imagine a simple way of realization. Can anybody propose a solution?
Thanks.
#import "DotView.h"
#implementation DotView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
center.x = center.y = 100.0;
}
return self;
}
- (void)drawRect:(NSRect)rect {
NSRect bounds = [self bounds];
[[NSColor whiteColor] set];
NSRectFill(bounds);
[[NSGraphicsContext currentContext]
setImageInterpolation: NSImageInterpolationHigh];
NSSize viewSize = [self bounds].size;
NSSize imageSize = { 50, 40 };
NSPoint imageOrigin = center;
imageOrigin.x -= imageSize.width * 0.50;
imageOrigin.y -= imageSize.height * 0.50;
NSRect destRect;
destRect.origin = imageOrigin;
destRect.size = imageSize;
NSString * file = #"/Users/classuser/Desktop/ded.jpg";
NSImage * image = [[NSImage alloc] initWithContentsOfFile:file];
[image setFlipped:NO];
[image drawInRect: destRect
fromRect: NSZeroRect
operation: NSCompositeSourceOver
fraction: 1.0];
NSBezierPath * path = [NSBezierPath bezierPathWithRect:destRect];
}
-(void)mouseDown:(NSEvent*)event
{
NSPoint point = [event locationInWindow];
if(center.x<point.x+25 && center.x>point.x-25)
if(center.y<point.y+20 && center.y>point.y-20)
center = [self convertPoint:point fromView:nil];
}
- (void) mouseDragged:(NSEvent*)event {
[self mouseDown:event];
[self setNeedsDisplay:YES];
}
#end
Read:
Cocoa Drawing Guide
and
View Programming Guide for Cocoa
and
Drag and Drop Programming Topics for Cocoa
Once you've read these, ask more targeted questions - this is a bit too broad to answer simply.