gpt4 book ai didi

cocoa - NSProgressIndicator 不在图层支持 View 中设置动画

转载 作者:行者123 更新时间:2023-12-03 16:13:50 25 4
gpt4 key购买 nike

我在图层支持 View 的 subview 中有一个条形NSProgressIndicator。它的行为有点复杂,但在某些时候,它显示为条形不确定进度指示器。问题是,在这种状态下,它不会产生动画(即旋转理发杆)。关闭图层支持可以解决这个问题,但这会使窗口的其他动画变得不那么平滑,所以我希望有更好的东西。

以下是完整的行为:当设置了相当于脏标志的内容时,它应该作为不确定的动画进度指示器可见;然后,经过短暂的延迟(以确保用户已完成输入)后,它会转换为确定的进度指示器,并在执行各种操作时填充;最后,在整个过程结束时,它再次隐藏自己。

为了实现这一点,我设置了以下绑定(bind):

  • Hidden 通过 NSNegateBoolean 值转换器绑定(bind)到我的模型的 loading 属性。
  • Is Inminated 绑定(bind)到我的模型的 waitingForInput 属性。
  • Value 绑定(bind)到我的模型的 currentProgress 属性(当 waitingForInput 为 true 时,该属性为 0)。
  • 最大值绑定(bind)到我的模型的 maximumProgress 属性(当 waitingForInput 为 true 时,该属性为 0)。

这在大多数情况下都有效,但有一个异常(exception):当 waitingForInputYES 时,进度指示器不确定,进度指示器不会显示动画。

进度指示器不更新的通常原因是程序员通过长时间运行的操作阻塞了运行循环,但我没有这样做:在相关期间,运行循环完全打开,只有一个计时器等待触发。据我所知,它也不处于某种奇怪的模式。该应用程序在此期间接受击键和其他事件,没有任何问题。 (后面的阶段,有一个确定的进度指示器填充,由异步 NSURLConnection 驱动,因此它也不会阻塞。)

我已采取了几个步骤来尝试解决此问题:

  • 我尝试将进度指示器上的 Animate 绑定(bind)设置为模型的 waitingForInput 属性,例如 Is Inminated。这会导致动画在 waitingForInput 上触发更改通知时更新不顺畅(waitingForInput 恰好在每次输入延迟重新启动时发送 KVO 通知),但我希望有更多比这更流畅的动画。
  • 我尝试使用 KVO 来观察 loadingwaitingForInput 的变化。当观察到变化时,它会根据需要调用进度指示器的 -startAnimation:-stopAnimation: 方法。这些没有明显的效果。
  • 我尝试将进度指示器上的 usesThreadedAnimation 设置为 NO。 (谷歌上的一次点击表明这可能有助于更新层支持的进度指示器的问题。)这没有明显的效果。我也尝试过YES,只是为了好玩,但事实证明同样是徒劳的。

最后,我还尝试过关闭图层背景。与 Animate 绑定(bind)结合使用时,这确实解决了该问题。但是,它会严重降低其他动画的性能,因此我宁愿避免这样做。

那么,大家有什么想法吗?我非常感谢您对这个问题的帮助!

最佳答案

没有任何解决方案不需要您...
a) 摸索 NSProgressIndicator 的内部结构或
b) Roll Your Own™。

所以我建议你应该提交一个错误。

至少在 OS X 10.6.5 及更高版本上,一旦您将不确定进度指示器的 wantsLayer 属性设置为 YES,动画就会立即停止 - 您可以使用简化的测试应用程序自行检查(代码如下)。

有一个名为 animate: 的方法(自 10.5 起已弃用),您可以在 NSProgressIndicator 上重复调用该方法,这可能可以帮助您(请参阅Using Indeterminate Progress Indicators)。

编辑:
从计时器仍然调用 animate: ,然后调用 displayIfNeeded(编辑 2: 正如 Brent 所说,这是多余的)作品。 “可能”只是意味着我不知道 App Store 是否允许使用已弃用的 API,或者这对您是否重要。

<小时/>

示例应用

带有一个 Controller 的简单 Cocoa 应用程序:

@interface ProgressTester : NSObject {
NSProgressIndicator *indicator;
}
@property (nonatomic, assign) IBOutlet NSProgressIndicator *indicator;
@property (nonatomic, assign, getter=isLayered) BOOL layered;

- (IBAction)toggleWantsLayer:(id)sender;
@end

@implementation ProgressTester
@synthesize indicator;
@dynamic layered;

- (BOOL)isLayered
{
return [indicator wantsLayer];
}
- (void)setLayered:(BOOL)wantsLayer
{
static NSString *layeredKey = @"layered";
[self willChangeValueForKey:layeredKey];
[indicator setWantsLayer:wantsLayer];
[self didChangeValueForKey:layeredKey];
}

- (void)awakeFromNib
{
// initialize/synchronize UI state
[self setLayered:NO];
[indicator startAnimation:self];
}

-(IBAction)toggleWantsLayer:(id)sender
{
self.layered = ! self.layered;
}
@end

在 NIB 中:

  1. Controller 实例
  2. 一个样式未定的 NSProgressIndicator(连接到 Controller 的 indicator socket )
  3. 以 Controller 为目标、以 toggleWantsLayer: 作为操作的按钮
<小时/>

由布伦特添加:

我使用此答案中的信息编写了 NSProgressIndicator 的简单子(monad)类:

http://www.pastie.org/1465755 http://www.pastie.org/1540277

请注意,在我的测试中,调用 -animate: 无需 -displayIfNeeded 即可正常工作。

您可以随意使用它。不过,如果您使用它,我很乐意收到您的来信!

<小时/>

由丹尼尔添加:

关于 Pastie 子类的几点:

  1. initWithFrame: 应调用 initWithFrame: 而不是 init (编辑 3:已在更新的代码片段中修复)。
  2. 不需要保留计时器:
    调度 NSTimer 会导致关联的运行循环保留并且不会释放它,直到计时器无效d
    (编辑3:也已修复)。
  3. 有一个强有力的候选者可以使用计时器进行保留循环:作为 NSTimer retains its target ,如果指示器在通过计时器动画时被释放,则 dealloc 可能永远不会被调用(我知道这是一个边缘情况,但是......)(编辑 3:也已处理) .
  4. 我不完全确定,但认为awakeFromNib的实现是多余的,因为KVO设置已经发生在initWithFrame中:(编辑 3:在更新的代码片段中进行了澄清)。

也就是说,我个人不喜欢合成 animationTimer 并在 setter 中处理计时器的失效,以完全摆脱 KVO 的东西。 (观察 self 有点超出了我的舒适区。)

<小时/>

安妮添加:

添加最新 Pastie 的片段 link用于存档目的:

ArchProgressIndicator.h

//
// ArchProgressIndicator.h
// Translate2
//
// Created by Brent Royal-Gordon on 1/15/11.
// Copyright 2011 Architechies. All rights reserved.
//

#import <Cocoa/Cocoa.h>


@interface ArchProgressIndicator : NSProgressIndicator {
@private
NSTimer * animationTimer;
}

// Just like NSProgressIndicator, but works better in a layer-backed view.

@end

ArchProgressIndicator.m

//
// ArchProgressIndicator.m
// Translate2
//
// Created by Brent Royal-Gordon on 1/15/11.
// Copyright 2011 Architechies. All rights reserved.
//

#import "ArchProgressIndicator.h"

@interface ArchProgressIndicator ()

@property (assign) NSTimer * animationTimer;

@end

@implementation ArchProgressIndicator

@synthesize animationTimer;

- (void)addObserver {
[self addObserver:self forKeyPath:@"animationTimer" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:[ArchProgressIndicator class]];
}

- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect])) {
[self addObserver];
}

return self;
}

// -initWithFrame: may not be called if created by a nib file
- (void)awakeFromNib {
[self addObserver];
}

// Documentation lists this as the default for -animationDelay
static const NSTimeInterval ANIMATION_UPDATE_INTERVAL = 5.0/60.0;

- (void)startAnimation:(id)sender {
[super startAnimation:sender];

if([self layer]) {
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:ANIMATION_UPDATE_INTERVAL target:self selector:@selector(animate:) userInfo:nil repeats:YES];
}
}

- (void)stopAnimation:(id)sender {
self.animationTimer = nil;

[super stopAnimation:sender];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(context == [ArchProgressIndicator class]) {
if([keyPath isEqual:@"animationTimer"]) {
if([change objectForKey:NSKeyValueChangeOldKey] != [NSNull null] && [change objectForKey:NSKeyValueChangeOldKey] != [change objectForKey:NSKeyValueChangeNewKey]) {
[[change objectForKey:NSKeyValueChangeOldKey] invalidate];
}
}
}
else {
return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

- (void)dealloc {
[self removeObserver:self forKeyPath:@"animationTimer"];

[animationTimer invalidate];

[super dealloc];
}

@end

关于cocoa - NSProgressIndicator 不在图层支持 View 中设置动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4662132/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com