Remarque: J'ai recherché Stack Overflow pour des problèmes similaires, et aucune des questions que j'ai trouvées ne semble résoudre ce problème particulier.La méthode NSTableView reloadData provoque la mise à jour de NSProgressIndicator dans toutes les lignes et le scintillement
J'ai écrit une petite application de l'échantillon (le projet Xcode avec le code source disponible ici: http://jollyroger.kicks-ass.org/stackoverflow/FlickeringTableView.zip) qui joue tous les sons en /System/Library/Sounds/
séquentielle et affiche les sons dans une fenêtre comme ils sont joués pour montrer la question que je je vois. La fenêtre de MainMenu.xib
a une seule colonne NSTableView
avec une ligne définie comme un modèle de cellule avec trois éléments qu'elle contient:
- un
NSTextField
pour contenir le nom du son - autre
NSTextField
pour contenir les informations sonores - un
NSProgressIndicator
pour montrer les progrès de jeu alors que le son joue
Je NSTableCellView
sous-classé (SoundsTableCellView.h
) pour définir chacun des éléments dans la cellule Vie w afin que je puisse accéder et les définir quand le moment se présente.
J'ai défini une classe MySound
qui encapsule les propriétés et les méthodes nécessaires pour gérer la lecture des fichiers audio via les API AVAudioPlayer
. Cette classe définit un protocole MySoundDelegate
pour permettre au délégué de l'application de recevoir des événements à chaque fois que les sons commencent ou se terminent.
Le délégué de l'application est conforme aux NSTableViewDelegate
et NSTableViewDataSource
protocoles pour permettre de stocker les données de la table comme un tableau de MySound
objets et mettre à jour le tableau des informations pertinentes en cas de besoin. Il adhère également au protocole MySoundDelegate
pour recevoir des événements lorsque les sons commencent ou finissent de jouer. Le délégué a également une tâche NSTimer
qui appelle périodiquement une méthode refreshWindow
pour mettre à jour l'indicateur de progression du son en cours de lecture. La méthode refreshWindow
du délégué de l'application affiche et redimensionne la fenêtre si nécessaire en fonction du nombre de sons de la liste, et met à jour la référence stockée au NSProgressIndicator
associé au son en cours de lecture.
La méthode tableView: viewForTableColumn
(NSTableViewDelegate
) du délégué de l'application est appelée pour remplir les cellules du tableau.Dans ce document, j'utilise la norme d'Apple « Remplir une table Voir Programmatically » conseil à:
- vérifier l'identifiant de colonne de table pour assurer qu'elle correspond à l'identificateur (
sound column
) Je réglerai Interface Builder (Xcode) pour la colonne de table , - obtenir la cellule du tableau correspondant à l'identificateur (
sound cell
) en appelantthisTableView makeViewWithIdentifier
, - utiliser le paramètre
row
entrant pour localiser l'élément de tableau de correspondance de la source de données (matrice de délégué d'applicationsounds
), puis - définir les valeurs de chaînes de
NSTextFields
et régler lamaxValue
etdoubleValue
duNSProgressIndicator
dans la cellule pour les détails correspondants de l'objet sonore associé, - stocker une référence à la commande
NSProgressIndicator
associée à l'objet audio associée pour la mise à jour plus tard
est ici la méthode :
- (NSView *)tableView:(NSTableView *)thisTableView viewForTableColumn:(NSTableColumn *)thisTableColumn row:(NSInteger)thisRow
{
SoundsTableCellView *cellView = nil;
// get the table column identifier
NSString *columnID = [thisTableColumn identifier];
if ([columnID isEqualToString:@"sound column"])
{
// get the sound corresponding to the specified row (sounds array index)
MySound *sound = [sounds objectAtIndex:thisRow];
// get an existing cell from IB with our hard-coded identifier
cellView = [thisTableView makeViewWithIdentifier:@"sound cell" owner:self];
// display sound name
[cellView.soundName setStringValue:[sound name]];
[cellView.soundName setLineBreakMode:NSLineBreakByTruncatingMiddle];
// display sound details (source URL)
NSString *details = [NSString stringWithFormat:@"%@", [sound sourceURL]];
[cellView.soundDetails setStringValue:details];
[cellView.soundDetails setLineBreakMode:NSLineBreakByTruncatingMiddle];
// update progress indicators
switch ([sound state])
{
case kMySoundStateQueued:
break;
case kMySoundStateReadyToPlay:
break;
case kMySoundStatePlaying:
if (sound.playProgress == nil)
{
sound.playProgress = cellView.playProgress;
}
NSTimeInterval duration = [sound duration];
NSTimeInterval position = [sound position];
NSLog(@"row %ld: %@ (%f/%f)", (long)thisRow, [sound name], position, duration);
NSLog(@" %@: %@", [sound name], sound.playProgress);
[cellView.playProgress setMaxValue:duration];
[cellView.playProgress setDoubleValue:position];
break;
case kMySoundStatePaused:
break;
case kMySoundStateFinishedPlaying:
break;
default:
break;
}
}
return cellView;
}
Et voici la méthode refreshWindow
:
- (void) refreshWindow
{
if ([sounds count] > 0)
{
// show window if needed
if ([window isVisible] == false)
{
[window makeKeyAndOrderFront:self];
}
// resize window to fit all sounds in the list if needed
NSRect frame = [self.window frame];
int screenHeight = self.window.screen.frame.size.height;
long maxRows = ((screenHeight - 22)/82) - 1;
long displayedRows = ([sounds count] > maxRows ? maxRows : [sounds count]);
long actualHeight = frame.size.height;
long desiredHeight = 22 + (82 * displayedRows);
long delta = desiredHeight - actualHeight;
if (delta != 0)
{
frame.size.height += delta;
frame.origin.y -= delta;
[self.window setFrame:frame display:YES];
}
// update play position of progress indicator for all sounds in the list
for (MySound *nextSound in sounds)
{
switch ([nextSound state])
{
case kMySoundStatePlaying:
if (nextSound.playProgress != nil)
{
[nextSound.playProgress setDoubleValue:[nextSound position]];
NSLog(@" %@: %@ position: %f", [nextSound name], nextSound.playProgress, [nextSound position]);
}
break;
case kMySoundStateQueued:
case kMySoundStateReadyToPlay:
case kMySoundStatePaused:
case kMySoundStateFinishedPlaying:
default:
break;
}
}
}
else
{
// hide window
if ([window isVisible])
{
[window orderOut:self];
}
}
// reload window table view
[tableView reloadData];
}
Pendant init
, le délégué de l'application scanne le dossier /System/Library/Sounds/
pour obtenir une liste des fichiers audio AIFF dans ce dossier, et crée un tableau sounds
tenant des objets sonores pour chacun des sons dans ce dossier. La méthode applicationDidFinishLaunching
démarre ensuite la lecture séquentielle du premier son de la liste. Le problème (que vous pouvez voir en exécutant l'exemple de projet) est qu'au lieu de mettre à jour uniquement la ligne de la table supérieure pour le son en cours de lecture, les indicateurs de progression tous les lignes semblent mettre à jour et scintillement aussi. La façon dont il est affiché est quelque peu incohérente (parfois ils clignotent tous, et parfois ils sont tous vierges comme prévu); mais quand ils se mettent à jour et scintillent, les indicateurs de progression semblent correspondre à peu près au son en cours de lecture. Je suis donc assez sûr que la question doit être en quelque sorte liée à la façon dont je suis mise à jour la table; Je ne suis pas sûr où le problème est ou comment le résoudre.
Voici une capture d'écran de ce que la fenêtre ressemble à vous donner une idée:
Toutes les idées ou conseils serait grandement apprécié!
Pour votre information: documentation "Remplir une table Voir Programmatically" d'Apple est ici: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TableView/PopulatingView-TablesProgrammatically/PopulatingView-TablesProgrammatically.html –
' makeViewWithIdentifier: owner: '" Retourne une vue nouvelle ou ** existante ** avec l'identifiant spécifié. ". Tous les sons peuvent pointer vers le même contrôle. – Willeke
Merci, @Willeke. J'ai vérifié, et il apparaît que l'indicateur de progression est bien différent de chaque ligne. J'ai ajouté à l'instruction 'NSLog' dans ma méthode' makeViewWithIdentifier' pour sortir la référence stockée dans 'NSProgressIndicator' pour l'objet sonore donné. Voici quelques exemple de sortie: Basso [] Coup [] Bottle [] Frog [] –