Не существует решения, которое бы не требовало от вас ...
а) шарить с внутренностями NSProgressIndicator
или
б) Roll Your Own ™.
Так что я бы сказал, что вы должны сообщить об ошибке.
По крайней мере в OS X 10.6.5 и выше, как только вы установите для свойства wantsLayer
индикатора неопределенного прогресса значение YES
, анимация немедленно остановится - вы можете проверить это сами с помощью уменьшенного теста. приложение (код ниже).
Был метод под названием animate:
(устарел с 10.5), который вы могли неоднократно вызывать на NSProgressIndicator
, который может помочь вам (см. Использование неопределенных индикаторов прогресса ).
Edit:
Вызов animate:
с последующим displayIfNeeded
( Edit 2: , как заметил Брент, это избыточно) из таймера все еще работает. «Может» просто означало, что я не знаю, разрешено ли использование устаревших API в App Store или это вообще важно для вас.
Пример приложения
Простое приложение 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
В СИБ:
- Экземпляр контроллера
- один NSProgressIndicator с неопределенным стилем (подключен к
indicator
выходу контроллера)
- кнопка с контроллером в качестве цели и
toggleWantsLayer:
в качестве действия
Добавлено Brent:
Я использовал информацию в этом ответе, чтобы написать простой подкласс NSProgressIndicator:
http://www.pastie.org/1465755 http://www.pastie.org/1540277
Обратите внимание, что в моих тестах вызов -animate:
работал без -displayIfNeeded
.
Не стесняйтесь использовать его по своему усмотрению. Я хотел бы услышать от вас, если вы используете это, хотя!
Добавлено от Daniel:
Несколько замечаний о подклассе на pastie:
initWithFrame:
должен вызывать initWithFrame:
вместо init
( Редактировать 3 : исправлено в обновленном фрагменте).
Таймер не нужно сохранять:
Планирование NSTimer
приводит к тому, что связанный runloop равняется retain
и не утилизируется, пока таймер не будет invalidate
d ( Edit 3 : также исправлено).
Существует сильный кандидат на сохранение цикла с таймером: так как NSTimer
сохраняет свою цель , dealloc, вероятно, никогда не будет вызван, если индикатор будет выпущен во время анимации через таймер (я знаю, что это крайний случай, но ...) ( Редактировать 3 : также позаботился).
Я не полностью уверен, но думаю, что реализация awakeFromNib
избыточна, так как настройка KVO уже произошла в initWithFrame:
( Редактировать 3 : уточнено в обновленном фрагменте).
Тем не менее, я лично предпочел бы не синтезировать animationTimer
и обрабатывать аннулирование таймера в установщике, чтобы вообще избавиться от KVO-вещей. (Наблюдение self
немного за пределами моей зоны комфорта.)
Добавлено Анной:
Добавление фрагмента из последней ссылки Pastie для целей архивирования:
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