我这两天有时候会想做一个向导对话框(就是那种上一步、下一步可以前进后退的玩意)。琢磨了一下,如果用Core Animation做出动画效果,那可就更好玩了。
基本上,这个例子做不了任何事。就是给你看看,一个窗口,里面有两个按钮:上一步、下一步,这样你可以通过前进后退的方式修改一些设置之类的东西。我做了几个NSView放在里面,它们按照不同顺序显示出来,就是这么个玩意。(就像安装向导一样,但是不同的是,我们要做的是一个有动画效果的向导对话框)
原文作者:Marcus Zarra
原文地址:
http://www.cimgf.com/2008/03/03/core-animation-tutorial-wizard-dialog-with-transitions/
做事不能费力气,我们用一些简单的方式去实现它。我写了一个NSView的子类,叫做MSZLinkedView。这里面我写了一些代码,包括“上一步”、“下一步”的视图连接和两个按钮。我直接在Interface Builder里面做了连接,这样就不需要什么代码了。
你可以在这里下载这个例子: Core Animation Wizard Tutorial Project.
MSZLinkedView.h
1 2 3 4 5 6 7 8 9 10 11 |
@interface MSZLinkedView : NSView { IBOutlet MSZLinkedView *previousView; IBOutlet MSZLinkedView *nextView;
IBOutlet NSButton *nextButton; IBOutlet NSButton *previousButton; }
@property(retain)MSZLinkedView *previousView, *nextView;
@end |
MSZLinkedView.m
1 2 3 4 5 6 7 8 9 10 11 12 |
@implementation MSZLinkedView
@synthesize previousView, nextView;
- (void)awakeFromNib { [self setWantsLayer:YES]; [previousButton setEnabled:(previousView != nil)]; [nextButton setEnabled:(nextView != nil)]; }
@end |
对于这个类来说,这些工作还不够。首先我用setWantsLayer:这个方法打开了视图的Core Animation支持。同时我在这里保留了两个视图, nextView和previousView,这样我就可以通过检查他们是否存在,而去禁用不需要的按钮。比如我走到最后一步的时候,那个“下一步”的按钮就不能按了,对吧?
创建 XIB/NIB
建立向导的下一个步骤是在Interface Builder里把用户需要看到的样子画出来。在这个例子里,我建了三个视图:1、2、3页(图里画的很清楚,三个窗口)。这三个页面每一个都有一个“上一步”、“下一步”按钮,和一个不同颜色的方框。如果你执行这个项目,你会看到,这三个窗口会以动画的形式滚动,而不需要的按钮将被禁用。
在Interface Builder里面给每一个视图里都连接了上一步、下一步两个视图和自带的两个按钮。这样的好处是在awakeFromNib执行之前,这些连接就会被启用,这样,不需要的按钮走到相应的步骤自然而然就会被禁用。
如果是一个真正的应用程序,我会在向导里的每页加入文字框、单选框、图片等等,但是在这个例子中我只是放了三个不同颜色的框做为演示用。就是让你在使用这个向导的时候看到页面的确改变了。
在这个例子里,应用程序托管保留了对于主窗口的参考,还仅仅保留了第一个选中的视图。因为这些视图是保存在列表中。AppDelegate完全不去管未被选中的视图,那是列表的任务。当nib文件被读取的时候,当前的view在awakeFromNib调用之前就会被留作参考。为了让这个视图做为一个子视图,我需要在awakeFromNib中加入它。(这段翻译的有点脑残,看不明白就请参考下面的代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@interface AppDelegate : NSObject { IBOutlet NSWindow *window; IBOutlet MSZLinkedView *currentView;
CATransition *transition; }
@property(retain)NSWindow *window; @property(retain)MSZLinkedView *currentView;
- (IBAction)nextView:(id)sender; - (IBAction)previousView:(id)sender;
@end |
Method breakdown of the AppDelegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- (void)awakeFromNib { NSView *contentView = [[self window] contentView]; [contentView setWantsLayer:YES]; [contentView addSubview:[self currentView]];
transition = [CATransition animation]; [transition setType:kCATransitionPush]; [transition setSubtype:kCATransitionFromLeft];
NSDictionary *ani = [NSDictionary dictionaryWithObject:transition forKey:@"subviews"]; [contentView setAnimations:ani]; } |
awakeFromNib
awakeFromNib首先打开Core Animation支持(就是setWantsLayer这行)。然后把当前的参考视图currentView做为子视图加入到contentView中。由于我们已经将currentView的frameOrigin属性设置为0,0,因此不需要考虑subview的位置。
接下来我建立了一个CATransition的动画。注意我在AppDelegate中将这个动画保留为ivar。原因很明显,当动画建立时,我将它做为transition动画加入content view,key是“subviews”。这个transition动画无论在一个subview添加、删除或者替换的时候都会触发。
1 2 3 4 5 6 7 8 9 10 |
- (void)setCurrentView:(MSZLinkedView*)newView { if (!currentView) { currentView = newView; return; } NSView *contentView = [[self window] contentView]; [[contentView animator] replaceSubview:currentView with:newView]; currentView = newView; } |
setCurrentView:(MSZLinkedView*)view
我重载这个方法的原因是为了方便。因为我只希望任何时候,当前屏幕上只有一个view,无论任何时候,当"currentView"的设置方法被调用时,我都可以轻松地切换。我可以用别的办法实现这个动画效果,但是由于我希望跟踪当前的view,这个是最好的办法。而且无论何时我希望切换窗口,我只需要把一个新的view传递到setCurrentView方法中就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (IBAction)nextView:(id)sender; { if (![[self currentView] nextView]) return; [transition setSubtype:kCATransitionFromRight]; [self setCurrentView:[[self currentView] nextView]]; }
- (IBAction)previousView:(id)sender; { if (![[self currentView] previousView]) return; [transition setSubtype:kCATransitionFromLeft]; [self setCurrentView:[[self currentView] previousView]]; } |
nextView:(id)sender previousView:(id)sender
这两个方法调用的方式是完全一样的。在这两个方法中,我把当前的view放入reference,同时检查上页、下页是否为空,目的仅仅是保护性的,因为我已经禁用了不需要的按钮。
检查完毕后,我调用了setCurrentView:,给它传递适当的view:或者是“上一步”,或者是“下一步”的页面。setCurrentView:方法上文中已经讨论过,用来切换视图。在我切换视图之前,我改变了transition的子类型,而这是我保留reference的原因。这样以来前进后退的动画效果才看上去是那么回事。上一页往左边移动,下一页往右边移动。这并不必要但是感觉会更好。
结论
Core Animation的transition在什么时候发生的呢?完全就在AppDelegate的awakeFromNib中。只需要简单的设置content view的CATransition,动画就出来了。其他的代码都是用来处理切换的,你只需要修改awakeFromNib中transition的类别和子类,就可以看到其他动画效果。