我试图为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];
}不用说,但是上面的代码根本不起作用。我看到的唯一效果是同一窗口上一个进度条的闪烁。这可能是因为我在试图“动画”它的时候阻塞了主运行循环。
请建议上述代码有什么问题。
附加说明:
setAnimatedStringValue总是由添加到主NSRunLoop中的NSTimer调用。
发布于 2013-01-06 15:58:50
我试试看:
我在这里发现了一些问题。首先,整件事情被设置为阻塞,所以它将阻塞主线程5秒。这将以SPOD/hang的形式转换给用户。你可能希望这是非阻塞,但它将需要一些额外的机器来实现这一点。
另外,您正在使用NSAnimationEaseOut作为淡出效果,这是由一个已知的bug影响的,它会导致动画向后运行。(在谷歌上搜索"NSAnimationEaseOut向后“,你可以看到很多人都遇到了这个问题。)在这个例子中,我对这两条曲线都使用了NSAnimationEaseIn。
我把它作为一个简单的非阻塞动画的例子。我不打算说这是理想的方法(我发布了第二个答案,可以说更好),但它是有效的,并有望成为你的起点。关键在于:
@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如果有基于块的API就好了.这样就省去了实现这个委托对象的必要。
我把整个项目都放在了GitHub上。
发布于 2013-01-06 16:28:00
在贴出之前的答案后,我四处打听了一下。我之所以留下这个答案,是因为它与您发布的代码非常对应,并且使用NSViewAnimation。然而,我确实想出了一个更加简洁的版本,尽管阅读起来有点困难(因为块参数缩进),而不是使用NSAnimationContext。这是:
#import <QuartzCore/QuartzCore.h>
@interface NSTextField (AnimatedSetString)
- (void) setAnimatedStringValue:(NSString *)aString;
@end
@implementation NSTextField (AnimatedSetString)
- (void) setAnimatedStringValue:(NSString *)aString
{
if ([[self stringValue] isEqual: aString])
{
return;
}
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setDuration: 1.0];
[context setTimingFunction: [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut]];
[self.animator setAlphaValue: 0.0];
}
completionHandler:^{
[self setStringValue: aString];
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[context setDuration: 1.0];
[context setTimingFunction: [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn]];
[self.animator setAlphaValue: 1.0];
} completionHandler: ^{}];
}];
}
@end注意:为了访问这里使用的用于指定非默认计时函数的CAMediaTimingFunction类,需要在项目中包含QuartzCore.framework。
也在GitHub上。
发布于 2017-05-12 21:36:01
对于Swift 3,下面是两个方便的setText()和setAttributedText()扩展方法,它们从一个文本淡出到另一个文本:
import Cocoa
extension NSTextField {
func setStringValue(_ newValue: String, animated: Bool = true, interval: TimeInterval = 0.7) {
guard stringValue != newValue else { return }
if animated {
animate(change: { self.stringValue = newValue }, interval: interval)
} else {
stringValue = newValue
}
}
func setAttributedStringValue(_ newValue: NSAttributedString, animated: Bool = true, interval: TimeInterval = 0.7) {
guard attributedStringValue != newValue else { return }
if animated {
animate(change: { self.attributedStringValue = newValue }, interval: interval)
}
else {
attributedStringValue = newValue
}
}
private func animate(change: @escaping () -> Void, interval: TimeInterval) {
NSAnimationContext.runAnimationGroup({ context in
context.duration = interval / 2.0
context.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
animator().alphaValue = 0.0
}, completionHandler: {
change()
NSAnimationContext.runAnimationGroup({ context in
context.duration = interval / 2.0
context.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
self.animator().alphaValue = 1.0
}, completionHandler: {})
})
}
}请按以下方式称呼他们:
var stringTextField: NSTextField
var attributedStringTextField: NSTextField
...
stringTextField.setStringValue("New Text", animated: true)
...
let attributedString = NSMutableAttributedString(string: "New Attributed Text")
attributedString.addAttribute(...)
attributedStringTextField.setAttributedStringValue(attributedString, animated: true)https://stackoverflow.com/questions/14174911
复制相似问题