4

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は常に a によって呼び出されNSTimer、これは main に追加されNSRunLoopます。

4

3 に答える 3

9

前の回答を投稿した後、私は少し突っついていました。投稿したコードに密接に対応し、を使用しているため、その回答は残しておきますNSViewAnimationNSAnimationContextただし、代わりに使用するバージョン(パラメーターのインデントをブロックするため)を読むのは少し難しいですが、私はかなり簡潔なものを思いつきました。ここで'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

注:このAPIを使用してデフォルト以外のタイミング関数を指定するためにここで使用されるクラスにアクセスするには、プロジェクトCAMediaTimingFunctionに含める必要があります。QuartzCore.framework

GitHubにもあります。

于 2013-01-06T16:28:00.527 に答える
1

私は刺します:

ここでいくつかの問題が見つかりました。まず、この全体がブロックするように設定されているため、メイン スレッドが 5 秒間ブロックされます。これは、SPOD/ハングとしてユーザーに変換されます。おそらくこれを非ブロッキングにしたいでしょうが、それを実現するには少し余分な機械が必要になります。

また、NSAnimationEaseOutフェードアウト効果にも使用しています。これは、アニメーションが逆方向に実行されるという既知のバグによって影響を受けます。(「NSAnimationEaseOut backwards」を Google で検索すると、多くの人がこの問題に遭遇したことがわかります。)NSAnimationEaseInこの例では、両方の曲線に使用しました。

非ブロッキングアニメーションを使用した簡単な例でこれを機能させました。これが理想的なアプローチであるとは言いませんが (私は間違いなくより良い 2 番目の回答を投稿しました)、うまく機能し、あなたの出発点として役立つことを願っています。要点は次のとおりです。

@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-06T15:58:50.530 に答える