注:我已搜查堆棧溢出類似的問題,並沒有我發現的問題似乎解決這方面的問題。NSTableView的reloadData方法使NSProgressIndicator中的所有行更新和閃爍
我寫了一個小樣本應用(完整的Xcode項目,可在這裏的源代碼:http://jollyroger.kicks-ass.org/stackoverflow/FlickeringTableView.zip)可以播放所有的聲音在/System/Library/Sounds/
依次顯示的聲音在一個窗口在播放時顯示的問題我我正在看。在MainMenu.xib
窗口有一個單柱NSTableView
在它定義爲一個單元模板,三個項目的一行:
- 的
NSTextField
持有聲音名 - 另一個
NSTextField
保持聲音細節 - 一個
NSProgressIndicator
顯示播放進度,同時播放聲音時
我有子類NSTableCellView
(SoundsTableCellView.h
)來定義每個項目在細胞VIE w以便我可以在時間到達時訪問和設置它們。
我已經定義了一個MySound
類,它封裝了通過AVAudioPlayer
API處理播放聲音文件所需的屬性和方法。該類定義了一個MySoundDelegate
協議,以允許應用程序委託在聲音開始或結束播放時接收事件。
的應用代表附着在NSTableViewDelegate
和NSTableViewDataSource
協議,以允許在需要時將表數據存儲爲MySound
對象的數組,並更新表相關的信息。它還遵守MySoundDelegate
協議,以在聲音開始或結束播放時接收事件。該代表還具有一個NSTimer
的任務,定期調用一個refreshWindow
方法來更新當前播放的聲音進度指示器。
應用程序代理的refreshWindow
方法根據列表中的聲音數量顯示並調整窗口的大小,並將存儲的參考更新爲關聯的NSProgressIndicator
以播放聲音。
調用應用程序代表的tableView: viewForTableColumn
(NSTableViewDelegate
協議)方法來填充表格單元格。在這裏面,我使用蘋果的標準「填充表視圖編程」建議:
- 檢查表列標識,以確保它的 標識符(
sound column
)匹配我在Interface Builder(的Xcode)爲表列設置, - 通過調用
thisTableView makeViewWithIdentifier
得到具有標識符(sound cell
)對應的表格單元格, - 使用傳入
row
參數來定位數據源(應用程序委託sounds
陣列)的匹配陣列元件 ,然後 - 設定的
NSTextFields
的字符串值,並設置maxValue
和在細胞中的相關聯的聲音對象的對應的細節的NSProgressIndicator
的doubleValue
, - 商店購買更新 到相關聯的
NSProgressIndicator
控制的基準在相關聯的聲音對象
這裏的方法:
- (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
,應用程序委託掃描/System/Library/Sounds/
文件夾來獲得該文件夾中的AIFF聲音文件的列表,並創建一個sounds
列保持爲每個文件夾中的聲音的聲音對象。然後applicationDidFinishLaunching
方法開始順序播放列表中的第一個聲音。
的問題(你可以通過運行示例項目看)是,而不是隻更新頂級錶行對於當前播放的進度指標以下行的所有聲音似乎更新和閃爍。它顯示的方式有些不一致(有時它們全部閃爍,有時候它們全都是空白的);但是當他們更新和閃爍時,進度指示器似乎大致對應於當前正在播放的聲音。所以我很確定這個問題必須與我更新表的方式有關。我只是不確定問題出在哪裏或如何解決。
這裏是什麼窗口的樣子給你一個想法的屏幕截圖:
任何意見或指導意見,將不勝感激!
僅供參考:Apple的「以編程方式填充表格視圖」文檔位於以下位置:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TableView/PopulatingView-TablesProgrammatically/PopulatingView-TablesProgrammatically.html –
' makeViewWithIdentifier:owner:'「用指定的標識符返回一個新的或**現有的**視圖。」所有的聲音都可以指向相同的控制。 – Willeke
謝謝,@Willeke。我檢查了一下,每行顯示進度指示器確實是_different_。我在我的'makeViewWithIdentifier'方法中添加了'NSLog'語句,將存儲的引用輸出到給定聲音對象的'NSProgressIndicator'。下面是一些示例輸出: 巴索[] 吹[] 瓶[] 青蛙[] –