如何在NSTextField中改变文本时对fadeIn fadeOut效果进行动画处理? [英] How do I animate fadeIn fadeOut effect in NSTextField when changing its text?
问题描述
我试图为 NSTextField
写一个类别,它会添加一个新方法 setAnimatedStringValue
。这个方法应该很好地淡出当前文本,然后设置新的文本,然后淡入。
下面是我的实现: -
- (void)setAnimatedStringValue:(NSString *)aString {
if([[self stringValue] isEqualToString:aString] ){
return;
}
NSMutableDictionary * dict = Nil;
NSViewAnimation * fadeOutAnim;
dict = [NSDictionary dictionaryWithObjectsAndKeys:self,NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect,NSViewAnimationEffectKey,nil]
fadeOutAnim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:
dict,nil]];
[fadeOutAnim setDuration:2];
[fadeOutAnim setAnimationCurve:NSAnimationEaseOut];
[fadeOutAnim setAnimationBlockingMode:NSAnimationBlocking];
NSViewAnimation * fadeInAnim;
dict = [NSDictionary dictionaryWithObjectsAndKeys:self,NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect,NSViewAnimationEffectKey,nil];
fadeInAnim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:
dict,nil]];
[fadeInAnim setDuration:3];
[fadeInAnim setAnimationCurve:NSAnimationEaseIn];
[fadeInAnim setAnimationBlockingMode:NSAnimationBlocking];
[fadeOutAnim startAnimation];
[self setStringValue:aString];
[fadeInAnim startAnimation];
}
不用说,但上述代码根本不工作。我看到的唯一效果是在同一个窗口上进度条的闪烁。这可能是因为我在阻止主runloop尝试动画它。
请建议上面的代码有什么问题。
其他附注:
setAnimatedStringValue
由 NSTimer
添加到 NSRunLoop
。
我会尝试一下:
我在这里发现了一些问题。首先,这整个事情被设置为阻塞,因此它将阻塞主线程5秒钟。这将转换为用户作为SPOD /挂起。你可能希望这是非阻塞,但它需要一点点额外的机械,使这种情况发生。
此外,您使用 NSAnimationEaseOut
获得淡出效果,这是由已知的错误导致动画向后运行。 (Google forNSAnimationEaseOut backwards,您可以看到很多都遇到了这个问题。)我使用 NSAnimationEaseIn
为这个例子的两个曲线。 / p>
我得到这个工作与一个微不足道的例子与非阻塞动画。我不会说这是理想方法(我发布了第二个答案,可以说更好),但它的工作,并希望可以作为一个跳跃点为你。这里是它的关键:
@interface NSTextField(AnimatedSetString)
- (void)setAnimatedStringValue: (NSString *)aString;
@end
@interface SOTextFieldAnimationDelegate:NSObject< NSAnimationDelegate>
- (id)initForSettingString:(NSString *)newString onTextField:(NSTextField *)tf;
@end
@implementation NSTextField(AnimatedSetString)
- (void)setAnimatedStringValue:(NSString *)aString
{
if([[self stringValue] isEqual:aString])
{
return;
}
[[[SOTextFieldAnimationDelegate alloc] initForSettingString:aString onTextField:self] autorelease];
}
@end
@implementation SOTextFieldAnimationDelegate
{
NSString * _newString;
NSAnimation * _fadeIn;
NSAnimation * _fadeOut;
NSTextField * _tf;
}
- (id)initForSettingString:(NSString *)newString onTextField:(NSTextField *)tf
{
if(self = [super init])
{
_newString = [newString copy];
_tf = [tf retain];
[self retain]; //当动画完成时,我们将自动释放自己。
_fadeOut = [[NSViewAnimation alloc] initWithViewAnimations:@ [(@ {
NSViewAnimationTargetKey:tf,
NSViewAnimationEffectKey:NSViewAnimationFadeOutEffect})]];
[_fadeOut setDuration:2];
[_fadeOut setAnimationCurve:NSAnimationEaseIn];
[_fadeOut setAnimationBlockingMode:NSAnimationNonblocking];
_fadeOut.delegate = self;
_fadeIn = [[NSViewAnimation alloc] initWithViewAnimations:@ [(@ {
NSViewAnimationTargetKey:tf,
NSViewAnimationEffectKey:NSViewAnimationFadeInEffect})]];
[_fadeIn setDuration:3];
[_fadeIn setAnimationCurve:NSAnimationEaseIn];
[_fadeInsetAnimationBlockingMode:NSAnimationNonblocking];
[_fadeOut startAnimation];
}
return self;
}
- (void)dealloc
{
[_newString release];
[_tf release];
[_fadeOut release];
[_fadeIn release];
[super dealloc]
}
- (void)animationDidEnd:(NSAnimation *)animation
{
if(_fadeOut == animation)
{
_fadeOut .delegate = nil;
[_fadeOut release];
_fadeOut = nil;
_tf.hidden = YES;
[_tf setStringValue:_newString];
_fadeIn.delegate = self;
[_fadeIn startAnimation];
}
else
{
_fadeIn.delegate = nil;
[_fadeIn release];
_fadeIn = nil;
[self autorelease];
}
}
@end
如果有基于块的API为这...它会节省必须实现这个委托对象真的很好。
我把整个项目放在< a href =https://github.com/ipmcc/SOExamples/tree/master/animation/AnimTextField =nofollow> GitHub 。
I am trying to write a category for NSTextField
which will add a new method setAnimatedStringValue
. This method is supposed to nicely fade-out the current text, then set the new text and then fade that in.
Below is my implementation:-
- (void) setAnimatedStringValue:(NSString *)aString {
if ([[self stringValue] isEqualToString:aString]) {
return;
}
NSMutableDictionary *dict = Nil;
NSViewAnimation *fadeOutAnim;
dict = [NSDictionary dictionaryWithObjectsAndKeys:self, NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey, nil];
fadeOutAnim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:
dict, nil]];
[fadeOutAnim setDuration:2];
[fadeOutAnim setAnimationCurve:NSAnimationEaseOut];
[fadeOutAnim setAnimationBlockingMode:NSAnimationBlocking];
NSViewAnimation *fadeInAnim;
dict = [NSDictionary dictionaryWithObjectsAndKeys:self, NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect, NSViewAnimationEffectKey, nil];
fadeInAnim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObjects:
dict, nil]];
[fadeInAnim setDuration:3];
[fadeInAnim setAnimationCurve:NSAnimationEaseIn];
[fadeInAnim setAnimationBlockingMode:NSAnimationBlocking];
[fadeOutAnim startAnimation];
[self setStringValue:aString];
[fadeInAnim startAnimation];
}
Needless to say, but the above code does not work at all. The only effect I see is the flickering of a progress bar on the same window. That is possibly because I am blocking the main runloop while trying to "animate" it.
Please suggest what is wrong with the above code.
Additional note:
setAnimatedStringValue
is always invoked by a NSTimer
, which is added to the main NSRunLoop
.
I'll take a stab:
I found a couple problems here. First off, this whole thing is set up to be blocking, so it's going to block the main thread for 5 seconds. This will translate to the user as a SPOD/hang. You probably want this to be non-blocking, but it'll require a little bit of extra machinery to make that happen.
Also, you're using NSAnimationEaseOut
for the fade out effect, which is effected by a known bug where it causes the animation to run backwards. (Google for "NSAnimationEaseOut backwards" and you can see that many have hit this problem.) I used NSAnimationEaseIn
for both curves for this example.
I got this working for a trivial example with non-blocking animations. I'm not going to say that this is the ideal approach (I posted a second answer that arguably better), but it works, and can hopefully serve as a jumping off point for you. Here's the crux of it:
@interface NSTextField (AnimatedSetString)
- (void) setAnimatedStringValue:(NSString *)aString;
@end
@interface SOTextFieldAnimationDelegate : NSObject <NSAnimationDelegate>
- (id)initForSettingString: (NSString*)newString onTextField: (NSTextField*)tf;
@end
@implementation NSTextField (AnimatedSetString)
- (void) setAnimatedStringValue:(NSString *)aString
{
if ([[self stringValue] isEqual: aString])
{
return;
}
[[[SOTextFieldAnimationDelegate alloc] initForSettingString: aString onTextField: self] autorelease];
}
@end
@implementation SOTextFieldAnimationDelegate
{
NSString* _newString;
NSAnimation* _fadeIn;
NSAnimation* _fadeOut;
NSTextField* _tf;
}
- (id)initForSettingString: (NSString*)newString onTextField: (NSTextField*)tf
{
if (self = [super init])
{
_newString = [newString copy];
_tf = [tf retain];
[self retain]; // we'll autorelease ourselves when the animations are done.
_fadeOut = [[NSViewAnimation alloc] initWithViewAnimations: @[ (@{
NSViewAnimationTargetKey : tf ,
NSViewAnimationEffectKey : NSViewAnimationFadeOutEffect})] ];
[_fadeOut setDuration:2];
[_fadeOut setAnimationCurve: NSAnimationEaseIn];
[_fadeOut setAnimationBlockingMode:NSAnimationNonblocking];
_fadeOut.delegate = self;
_fadeIn = [[NSViewAnimation alloc] initWithViewAnimations: @[ (@{
NSViewAnimationTargetKey : tf ,
NSViewAnimationEffectKey : NSViewAnimationFadeInEffect})] ];
[_fadeIn setDuration:3];
[_fadeIn setAnimationCurve:NSAnimationEaseIn];
[_fadeIn setAnimationBlockingMode:NSAnimationNonblocking];
[_fadeOut startAnimation];
}
return self;
}
- (void)dealloc
{
[_newString release];
[_tf release];
[_fadeOut release];
[_fadeIn release];
[super dealloc];
}
- (void)animationDidEnd:(NSAnimation*)animation
{
if (_fadeOut == animation)
{
_fadeOut.delegate = nil;
[_fadeOut release];
_fadeOut = nil;
_tf.hidden = YES;
[_tf setStringValue: _newString];
_fadeIn.delegate = self;
[_fadeIn startAnimation];
}
else
{
_fadeIn.delegate = nil;
[_fadeIn release];
_fadeIn = nil;
[self autorelease];
}
}
@end
It would be really nice if there were block-based API for this... it'd save having to implement this delegate object.
I put the whole project up on GitHub.
这篇关于如何在NSTextField中改变文本时对fadeIn fadeOut效果进行动画处理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!