注意:我搜索过Stack Overflow是否存在类似的问题,我发现的任何问题似乎都没有解决这个问题。
我已经编写了一个小样本应用程序(完整的Xcode项目,源代码可在此处http://jollyroger.kicks-ass.org/stackoverflow/FlickeringTableView.zip),它可以按顺序播放/System/Library/Sounds/
中的所有声音并在窗口中显示声音,因为它们是用来表明我所看到的问题。 MainMenu.xib
中的窗口有一个单列NSTableView
,其中一行定义为一个单元格模板,其中包含三个项目:
NSTextField
来保存声音名称NSTextField
来保存声音细节NSProgressIndicator
以显示正在播放声音时的播放进度我已经将NSTableCellView
(SoundsTableCellView.h
)子类化,以定义单元格视图中的每个项目,以便我可以在时间出现时访问和设置它们。
我已经定义了一个MySound
类,它封装了通过AVAudioPlayer
API处理声音文件播放所需的属性和方法。此类定义了MySoundDelegate
协议,以允许应用程序委托在声音开始或结束播放时接收事件。
应用程序委托遵守NSTableViewDelegate
和NSTableViewDataSource
协议,允许它将表数据存储为MySound
个对象的数组,并在需要时使用相关信息更新表。它还遵循MySoundDelegate
协议,以便在声音开始或结束播放时接收事件。该委托还有一个NSTimer
任务,定期调用refreshWindow
方法来更新当前播放声音的进度指示器。
app delegate的refreshWindow
方法根据列表中的声音数量显示窗口并调整窗口大小,并将存储的引用更新为正在播放的声音的关联NSProgressIndicator
。 / p>
调用app委托的tableView: viewForTableColumn
(NSTableViewDelegate
协议)方法来填充表格单元格。在其中,我使用Apple的标准“以程序方式填充表格视图”建议:
sound column
)我在Interface Builder(Xcode)中为表列设置了sound cell
,thisTableView makeViewWithIdentifier
)的相应表格单元格
row
参数来定位匹配的数组元素
数据源(app delegate sounds
数组),然后NSTextFields
的字符串值,并将单元格中maxValue
的{{1}}和doubleValue
设置为相关声音对象的相应详细信息,NSProgressIndicator
控件的引用,以便以后更新以下是NSProgressIndicator
方法:
viewForTableColumn
这是- (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;
}
方法:
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];
}
期间,应用程序委托扫描init
文件夹以获取该文件夹中的AIFF声音文件列表,并创建一个/System/Library/Sounds/
数组,其中包含每个声音的声音对象在那个文件夹中。然后sounds
方法按顺序开始播放列表中的第一个声音。
问题(通过运行示例项目可以看到)不是仅更新当前正在播放的声音的顶部表行,而是在以下行的 all 中的进度指示器似乎也更新和闪烁。它显示的方式有些不一致(有时它们都闪烁,有时它们都是预期的空白);但是当他们确实更新并闪烁时,进度指示器似乎大致对应于当前正在播放的声音。所以我很确定这个问题必须与我更新表的方式有某种关系;我只是不确定问题出在哪里或如何解决。
这是一个屏幕截图,显示了窗口的样子,可以给你一个想法:
非常感谢任何想法或指导!
答案 0 :(得分:0)
这是我改变的。
tableView:viewForTableColumn:row:
返回“显示指定行和列的视图”。始终设置进度条的值。
- (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]];
// display sound details (source URL)
NSString *details = [NSString stringWithFormat:@"%@", [sound sourceURL]];
[cellView.soundDetails setStringValue:details];
// update progress indicators
// [cellView.playProgress setUsesThreadedAnimation:NO];
NSTimeInterval duration = [sound duration];
NSTimeInterval position = [sound position];
[cellView.playProgress setMaxValue:duration];
[cellView.playProgress setDoubleValue:position];
}
// end updates
// [thisTableView endUpdates];
return cellView;
}
refreshWindow
分为refreshProgress
和refreshWindow
。 refreshProgress
刷新播放声音的行,并在计时器上调用。
- (void)refreshProgress
{
if ([sounds count] > 0)
{
[sounds enumerateObjectsUsingBlock:^(MySound *nextSound, NSUInteger rowNr, BOOL *stop)
{
switch ([nextSound state])
{
case kMySoundStatePlaying:
// refresh row
[tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:rowNr]
columnIndexes:[NSIndexSet indexSetWithIndex:0]];
break;
case kMySoundStateQueued:
case kMySoundStateReadyToPlay:
case kMySoundStatePaused:
case kMySoundStateFinishedPlaying:
default:
break;
}
}];
}
}
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
... calculate new window frame
}
else
{
// hide window
if ([window isVisible])
{
[window orderOut:self];
}
}
}
当声音被删除时,该行也会被删除,因此其他行仍会显示相同的声音而不需要更新。
- (void) soundFinishedPlaying:(MySound *)sound encounteredError:(NSError *)error
{
if (error != NULL)
{
// display an error dialog box to the user
[NSApp presentError:error];
}
else
{
// remove sound from array
NSLog(@"deleting: [%@|%@]", [sound truncatedID], [sound name]);
NSUInteger index = [sounds indexOfObject:sound];
[sounds removeObject:sound];
[tableView removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:index] withAnimation:NSTableViewAnimationEffectNone];
}
// refresh window
[self refreshWindow];
// play the next sound in the queue
[self play];
}
[tableView reloadData]
未被调用。 <{1}}未使用。