[英]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
調用,該NSTimer
被添加到主NSRunLoop
。
在發布上一個答案之后,我有點蠢蠢欲動。 我將離開那個答案,因為它與您發布的代碼緊密對應,並使用NSViewAnimation
。 但是,我確實提出了一個更簡潔,盡管稍微難以閱讀(由於塊參數縮進)版本,而是使用NSAnimationContext
。 這是'tis:
#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
類以使用此API指定非默認計時函數,您需要在項目中包含QuartzCore.framework
。
也在GitHub上 。
對於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)
我會采取刺:
我在這里發現了幾個問題。 首先,整個事情被設置為阻塞,所以它將阻塞主線程5秒。 這將轉換為SPOD /掛起的用戶。 你可能希望這是非阻塞的,但它需要一些額外的機制來實現這一點。
此外,您正在使用NSAnimationEaseOut
進行淡出效果,這會受到已知錯誤的影響,導致動畫向后運行。 (谷歌的“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上 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.