NSS滑块动画

发布于 2024-12-29 02:50:10 字数 168 浏览 1 评论 0 原文

更改 NSSlider 的浮点值时如何创建 NSSlider 动画。我正在尝试:

[[mySlider animator] setFloatValue:-5];

但这不起作用..只需更改没有动画的值。那么也许有人知道该怎么做?

提前致谢。

How can I create NSSlider animation when changing float value of it. I was trying:

[[mySlider animator] setFloatValue:-5];

but that didn't work.. just change the value without animation. So maybe someone knows how to do this?

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

厌倦 2025-01-05 02:50:10

好吧 - 所以这并不像我希望的那么快和漂亮,但它有效。
您实际上无法在滑块旋钮上使用动画器和核心动画 - 因为核心动画仅适用于图层,并且无法访问滑块图层中的旋钮值。

因此,我们必须求助于手动设置滑块值的动画。
由于我们是在 Mac 上执行此操作 - 您可以使用 NSAnimation(在 iOS 上不可用)。

NSAnimation 的作用很简单 - 它提供了一种计时/插值机制,允许您制作动画(与 Core Animation 不同,Core Animation 也连接到视图并处理对它们的更改)。

要使用 NSAnimation - 最常见的是对其进行子类化并覆盖 setCurrentProgress:
并把你的逻辑放在那里。

以下是我实现此方法的方法 - 我创建了一个名为 NSAnimationForSlider 的新 NSAnimation 子类

NSAnimationForSlider.h :

@interface NSAnimationForSlider : NSAnimation  
{  
    NSSlider *delegateSlider;  
    float animateToValue;    
    double max;   
    double min;  
    float initValue;  
}  
@property (nonatomic, retain) NSSlider *delegateSlider;  
@property (nonatomic, assign) float animateToValue;    
@end  

NSAnimationForSlider.m :

#import "NSAnimationForSlider.h"

@implementation NSAnimationForSlider
@synthesize delegateSlider;
@synthesize animateToValue;

-(void)dealloc
{
    [delegateSlider release], delegateSlider = nil;
}

-(void)startAnimation
{
    //Setup initial values for every animation
    initValue = [delegateSlider floatValue];
    if (animateToValue >= initValue) {
        min = initValue;
        max = animateToValue;
    } else  {
        min = animateToValue;
        max = initValue;
    }

    [super startAnimation];
}


- (void)setCurrentProgress:(NSAnimationProgress)progress
{
    [super setCurrentProgress:progress];

    double newValue;
    if (animateToValue >= initValue) {
        newValue = min + (max - min) * progress;        
    } else  {
        newValue = max - (max - min) * progress;
    }

    [delegateSlider setDoubleValue:newValue];
}

@end

要使用它 - 您只需创建一个新的 NSAnimationForSlider ,将您正在使用的滑块作为delegate 并在每个动画之前设置它的 animateToValue 然后启动动画。

例如:

slider = [[NSSlider alloc] initWithFrame:NSMakeRect(50, 150, 400, 25)];
[slider setMaxValue:200];
[slider setMinValue:50];
[slider setDoubleValue:50];

[[window contentView] addSubview:slider];

NSAnimationForSlider *sliderAnimation = [[NSAnimationForSlider alloc] initWithDuration:2.0 animationCurve:NSAnimationEaseIn];
[sliderAnimation setAnimationBlockingMode:NSAnimationNonblocking];
[sliderAnimation setDelegateSlider:slider];
[sliderAnimation setAnimateToValue:150];

[sliderAnimation startAnimation];

Ok - so this isn't as quick and pretty as I hoped but it works.
You can't actually use animators and Core Animation on the slider knob - because Core Animation works only on layers and there's no access to the knob values in the slider layer.

So we have to resort instead to manually animating slider value.
Since we're doing this on a Mac - you can use NSAnimation (which isn't available on iOS).

What NSAnimation does is simple - it provide an timing/interpolation mechanism to allow YOU to animate (as opposed to Core Animation which also connects to the views and handles the changes to them).

To use NSAnimation - you most commonly would subclass it and override setCurrentProgress:
and put your logic in there.

Here's how I implemented this - I created a new NSAnimation subclass called NSAnimationForSlider

NSAnimationForSlider.h :

@interface NSAnimationForSlider : NSAnimation  
{  
    NSSlider *delegateSlider;  
    float animateToValue;    
    double max;   
    double min;  
    float initValue;  
}  
@property (nonatomic, retain) NSSlider *delegateSlider;  
@property (nonatomic, assign) float animateToValue;    
@end  

NSAnimationForSlider.m :

#import "NSAnimationForSlider.h"

@implementation NSAnimationForSlider
@synthesize delegateSlider;
@synthesize animateToValue;

-(void)dealloc
{
    [delegateSlider release], delegateSlider = nil;
}

-(void)startAnimation
{
    //Setup initial values for every animation
    initValue = [delegateSlider floatValue];
    if (animateToValue >= initValue) {
        min = initValue;
        max = animateToValue;
    } else  {
        min = animateToValue;
        max = initValue;
    }

    [super startAnimation];
}


- (void)setCurrentProgress:(NSAnimationProgress)progress
{
    [super setCurrentProgress:progress];

    double newValue;
    if (animateToValue >= initValue) {
        newValue = min + (max - min) * progress;        
    } else  {
        newValue = max - (max - min) * progress;
    }

    [delegateSlider setDoubleValue:newValue];
}

@end

To use it - you simply create a new NSAnimationForSlider, give it the slider you are working on as a delegate and before each animation you set it's animateToValue and then just start the animation.

For example:

slider = [[NSSlider alloc] initWithFrame:NSMakeRect(50, 150, 400, 25)];
[slider setMaxValue:200];
[slider setMinValue:50];
[slider setDoubleValue:50];

[[window contentView] addSubview:slider];

NSAnimationForSlider *sliderAnimation = [[NSAnimationForSlider alloc] initWithDuration:2.0 animationCurve:NSAnimationEaseIn];
[sliderAnimation setAnimationBlockingMode:NSAnimationNonblocking];
[sliderAnimation setDelegateSlider:slider];
[sliderAnimation setAnimateToValue:150];

[sliderAnimation startAnimation];
若能看破又如何 2025-01-05 02:50:10

你的方法有效,但还有更简单的方法。

可以使用动画代理,您只需告诉它如何制作动画即可。
为此,您需要实现 NSAnimatablePropertyContainer 协议中的 defaultAnimationForKey: 方法。


这是 NSSlider 的一个简单子类,它可以执行此操作:

#import "NSAnimatableSlider.h"
#import <QuartzCore/QuartzCore.h>

@implementation NSAnimatableSlider

+ (id)defaultAnimationForKey:(NSString *)key
{
    if ([key isEqualToString:@"doubleValue"]) {
        return [CABasicAnimation animation];
    } else {
        return [super defaultAnimationForKey:key];
    }
}

@end

现在您可以简单地使用动画代理:

[self.slider.animator setDoubleValue:100.0];

确保链接 QuartzCore 框架。

Your method works, but there's something much simpler.

You can use the animator proxy, you just need to tell it how to animate it.
To do this, you need to implement the defaultAnimationForKey: method from the NSAnimatablePropertyContainer protocol.


Here's a simple subclass of NSSlider that does this:

#import "NSAnimatableSlider.h"
#import <QuartzCore/QuartzCore.h>

@implementation NSAnimatableSlider

+ (id)defaultAnimationForKey:(NSString *)key
{
    if ([key isEqualToString:@"doubleValue"]) {
        return [CABasicAnimation animation];
    } else {
        return [super defaultAnimationForKey:key];
    }
}

@end

Now you can simply use the animator proxy:

[self.slider.animator setDoubleValue:100.0];

Make sure to link the QuartzCore framework.

无名指的心愿 2025-01-05 02:50:10

这是 IluTov 答案 的 Swift 版本,使用一些动画配置设置 floatValue

override class func defaultAnimation(forKey key: NSAnimatablePropertyKey) -> Any? {
    if key == "floatValue" {
        let animation = CABasicAnimation()
        animation.timingFunction = .init(name: .easeInEaseOut)
        animation.duration = 0.2
        
        return animation
    } else {
        return super.defaultAnimation(forKey: key)
    }
}

Here is a Swift version of IluTov answer, setting a floatValue with some animation config:

override class func defaultAnimation(forKey key: NSAnimatablePropertyKey) -> Any? {
    if key == "floatValue" {
        let animation = CABasicAnimation()
        animation.timingFunction = .init(name: .easeInEaseOut)
        animation.duration = 0.2
        
        return animation
    } else {
        return super.defaultAnimation(forKey: key)
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文