4 Stimmen

NSProgressIndicator animiert nicht in der Ansicht mit Ebenenrückseite

Ich habe einen Bar-Stil NSProgressIndicator in einer Unteransicht einer Ansicht mit Ebenenrückseite. Sein Verhalten ist etwas kompliziert, aber an bestimmten Punkten wird er als unbestimmter Fortschrittsanzeiger in Form eines Balkens angezeigt. Das Problem ist, wenn in diesem Zustand, es nicht animieren (dh wirbeln die Barbier Stange). Das Ausschalten der Ebenenunterstützung behebt das Problem, aber das macht andere Animationen des Fensters weniger glatt, also hoffe ich auf etwas Besseres.

Hier ist das vollständige Verhalten: Wenn ein Dirty Flag gesetzt wird, sollte es als unbestimmter, animierter Fortschrittsindikator sichtbar werden; dann, nach einer kurzen Verzögerung (um sicherzustellen, dass der Benutzer mit dem Tippen fertig ist), verwandelt es sich in einen bestimmten Fortschrittsindikator und füllt sich, während verschiedene Operationen ausgeführt werden; und schließlich, am Ende des gesamten Prozesses, versteckt es sich wieder.

Um dies zu realisieren, habe ich die folgenden Bindungen eingerichtet:

  • Versteckt ist an mein Modell gebunden loading Eigenschaft mit einer NSNegateBoolean Werttransformator.
  • Ist unbestimmt ist an mein Modell gebunden waitingForInput Eigentum.
  • Wert ist an mein Modell gebunden currentProgress Eigenschaft (die 0 ist, wenn waitingForInput wahr ist).
  • Maximaler Wert ist an mein Modell gebunden maximumProgress Eigenschaft (die 0 ist, wenn waitingForInput wahr ist).

Das funktioniert meistens, aber mit einer Ausnahme: Wenn waitingForInput es YES und somit die Fortschrittsanzeige unbestimmt ist, wird die Fortschrittsanzeige nicht animiert.

Der übliche Grund dafür, dass eine Fortschrittsanzeige nicht aktualisiert wird, ist, dass der Programmierer die Ablaufschleife mit einer lang andauernden Operation blockiert, aber das tue ich nicht: Während des fraglichen Zeitraums ist die Ablaufschleife völlig offen, und nur ein Timer wartet auf den Start. Soweit ich weiß, befindet sie sich auch nicht in einem merkwürdigen Modus. Die Anwendung akzeptiert während dieser Zeit ohne Probleme Tastatureingaben und andere Ereignisse. (Die spätere Phase, in der sich eine bestimmte Fortschrittsanzeige füllt, wird von einem asynchronen NSURLConnection Es wird also auch nicht blockiert).

Ich habe mehrere Schritte unternommen, um dieses Problem zu beheben:

  • Ich habe versucht, die Einstellung Animieren Bindung des Fortschrittsindikators an das Modell waitingForInput Eigenschaft, wie Ist unbestimmt . Dies führt dazu, dass die Animation ruckartig aktualisiert wird, wenn Änderungsbenachrichtigungen auf waitingForInput ( waitingForInput jedes Mal KVO-Benachrichtigungen sendet, wenn die Eingabeverzögerung neu startet), aber ich hoffe auf eine viel flüssigere Animation als das.
  • Ich habe versucht, mit Hilfe von KVO die Änderungen an beiden loading y waitingForInput . Wenn eine Änderung beobachtet wird, ruft es die Fortschrittsanzeige -startAnimation: y -stopAnimation: Methoden nach Bedarf. Diese haben keine offensichtlichen Auswirkungen.
  • Ich habe versucht, die Einstellung usesThreadedAnimation auf den Fortschrittsindikator zu NO . (Ein Google-Treffer deutete darauf hin, dass dies bei Aktualisierungsproblemen von Fortschrittsindikatoren auf Ebenen helfen könnte). Dies hat keine offensichtliche Wirkung. Ich habe auch versucht YES nur so zum Spaß, was sich als ebenso sinnlos erwies.

Schließlich habe ich auch versucht, die Ebenenunterstützung zu deaktivieren. Dies behebt das Problem in Kombination mit der Animate-Bindung. Allerdings verschlechtert es die Leistung anderer Animationen inakzeptabel, so dass ich es vorziehen würde, dies zu vermeiden.

Also, hat jemand eine Idee? Ich wäre wirklich dankbar für etwas Hilfe bei diesem Problem!

5voto

danyowdee Punkte 4618

Es gibt keine Lösung, die nicht auch von Ihnen verlangt, dass Sie...
a) sich mit den Interna von NSProgressIndicator ou
b) Roll Your Own™.

Ich würde also sagen, Sie sollten einen Fehler melden.

Zumindest unter OS X 10.6.5 und höher, sobald Sie den Wert für den unbestimmten Fortschrittsindikator auf wantsLayer Eigenschaft zu YES stoppt die Animation sofort - Sie können das mit einer reduzierten Test-App (Code unten) selbst überprüfen.

Es gab eine Methode namens animate: (veraltet seit 10.5), die Sie wiederholt aufrufen können auf NSProgressIndicator die Mai Ihnen helfen (siehe Verwendung unbestimmter Fortschrittsindikatoren ).

Bearbeiten:
Aufruf von animate: gefolgt von displayIfNeeded ( Bearbeiten 2: wie Brent feststellte, ist dies überflüssig) von einer Zeitschaltuhr aus noch funktioniert. Das "kann" bedeutete lediglich, dass ich nicht weiß, ob die Verwendung veralteter APIs im App Store erlaubt ist oder ob dies für Sie überhaupt von Bedeutung ist.


Beispiel-App

Einfache Cocoa-Anwendung mit einem Controller:

@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

In der NIB:

  1. Instanz des Controllers
  2. ein NSProgressIndicator mit unbestimmtem Stil (verbunden mit dem indicator Ausgang des Controllers)
  3. eine Schaltfläche mit dem Controller als Ziel und toggleWantsLayer: als Aktion

Hinzugefügt von Brent:

Ich habe die Informationen in dieser Antwort verwendet, um eine einfache Unterklasse von NSProgressIndicator zu schreiben:

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

Beachten Sie, dass in meinen Tests der Aufruf von -animate: funktionierte gut ohne -displayIfNeeded .

Sie können sie nach eigenem Ermessen verwenden. Ich würde mich aber freuen, von Ihnen zu hören, wenn Sie es verwenden!


Hinzugefügt von Daniel:

Ein paar Punkte zur Unterklasse auf Pastie:

  1. initWithFrame: sollte durchrufen zu initWithFrame: 代わりに init ( Bearbeiten 3 : in aktualisiertem Snippet behoben).
  2. ~~Der Timer muss nicht aufbewahrt werden:
    Planen einer NSTimer bewirkt, dass die zugehörige Runloop retain und erst dann zu entsorgen, wenn der Timer invalidate d~~ ( Bearbeiten 3 : ebenfalls behoben).
  3. Es gibt einen starken Kandidaten für einen Retain-Zyklus mit dem Timer: Als ein NSTimer behält sein Ziel bei dealloc wird wahrscheinlich nie aufgerufen, wenn der Indikator losgelassen wird, während er durch den Timer animiert wird (ich weiß, es ist ein Sonderfall, aber...) ( Bearbeiten 3 : Auch dafür ist gesorgt).
  4. Ich bin nicht vollständig sicher, aber ich denke, dass die Umsetzung der awakeFromNib ist überflüssig, da die KVO-Einrichtung bereits in initWithFrame: ( Bearbeiten 3 : in dem aktualisierten Ausschnitt klargestellt).

Allerdings würde ich persönlich es vorziehen, nicht zu synthetisieren. animationTimer und die Ungültigkeitserklärung des Timers im Setter behandeln, um den KVO-Kram loszuwerden. (Beobachtung self liegt ein wenig außerhalb meiner Komfortzone.)


Hinzugefügt von Anne:

Schnipsel aus dem letzten Pastie einfügen Link für Archivierungszwecke:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X