2015-09-04 61 views
2

我想使用NSArrayControllerNSTableView來允許多選,但只有在選擇單個對象時才提供選定的對象(並且當選擇一個或多個對象時爲nil) 。爲什麼我的KVO依賴項在NSArrayController中不起作用

我已經嘗試與NSArrayController類別來實現這一點,如下所示:

@implementation NSArrayController (SelectedObject) 

+ (NSSet *)keyPathsForValuesAffectingSelectedObject { 
    return [NSSet setWithObject:@"selection"]; 
} 

- (id)selectedObject { 
    // Get the actual selected object (or nil) instead of a proxy. 
    if (self.selectionIndexes.count == 1) { 
     return [self arrangedObjects][self.selectionIndex]; 
    } 
    return nil; 
} 

@end 

出於某種原因,selectedObject方法不調用時的陣列控制器改變選擇(和別的東西正在觀察selectedObject)。爲什麼是這樣?

回答

0

我設法通過創建NSArrayController的子類並手動觀察selectionIndexes鍵來獲得此工作。我寧願使用類別來做,但這似乎確實有效。

static NSString *const kObservingSelectionIndexesContext = @"ObservingSelectionIndexesContext"; 

@implementation BetterArrayController 

- (void)awakeFromNib { 
    [super awakeFromNib]; 
    [self addObserver:self forKeyPath:@"selectionIndexes" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew context:(void *)&kObservingSelectionIndexesContext]; 
} 

- (void)dealloc { 
    [self removeObserver:self forKeyPath:@"selectionIndexes" context:(void *)&kObservingSelectionIndexesContext]; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
    if (context == (void *)&kObservingSelectionIndexesContext) { 
     [self willChangeValueForKey:@"selectedObject"]; 
     [self didChangeValueForKey:@"selectedObject"]; 
    } else { 
     [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 
    } 
} 

- (id)selectedObject { 
    // Get the actual selected object (or nil) instead of a proxy. 
    if (self.selectionIndexes.count == 1) { 
     return [self arrangedObjects][self.selectionIndex]; 
    } 
    return nil; 
} 

@end 

我使用的上下文(如每this article),以避免除去任何觀察員超類可以具有dealloc(作爲對抗here警告)。

+0

在'-awakeFromNib'中添加自我觀察看起來並不完全可靠。爲什麼不重寫'-initWithContent:'?在'-observeValueForKeyPath:...'中,如果上下文是你的,不要通過超級調用。使'kObservingSelectionIndexesContext''NSString * const'。在基礎屬性已經改變之後執行'willChange ...'是不安全/可靠的。使用'NSKeyValueObservingOptionPrior',並根據「change」是否包含「NSKeyValueChangeNotificationIsPriorKey」調用'willChange ...'或'didChange ...'。這就是它的目的。 –

+0

是否保證'initWithContent'始終被調用?如果它是用'initWithCoder:'初始化的呢?此外,它看起來像NSKeyValueObservingOptionPrior [不適用於NSArrayController](https://lists.apple.com/archives/Cocoa-dev/2010/Apr/msg00092.html)([radar](https:// openradar。 appspot.com/7834918))。 – DanielGibbs

+0

'-initWithContent:'是'NSObjectController'的指定初始值,'NSArrayController'從其繼承。 'NSArrayController'不聲明一個新的指定初始化器。所有其他初始化器都必須通過指定的初始化器以良好行爲的類進行路由。所以,是的,它確保了'NSArrayController'或'NSObjectController'中的模塊錯誤,正如我們所看到的那樣,它不是不可能的。 –

1

屬性NSArrayController是奇怪的巫術。我不知道是否鍵值觀察它(而不是一個從它的路徑),當選擇改變時產生改變通知。畢竟,它會返回一個代理,並且沒有理由相信該代理的身份會隨着時間而改變。

在任何情況下,您的實際selectedObject方法實際上並不使用selection(它不應該)。它使用arrangedObjectsselectionIndexes。所以,你應該返回一組包含那些密鑰從+keyPathsForValuesAffectingSelectedObject。當然,如果你使用的是基於視圖的表,你需要確保表視圖的selectionIndexes綁定綁定到數組控制器的selectionIndexes屬性,或者數組控制器不知道任何關於選擇的內容在表格視圖中。 (對於基於單元格的表格視圖,您通常會將列綁定到數組控制器,並且表視圖會自動綁定基於列綁定的綁定。)

最後,我認爲您應該選擇不同的名稱爲selectedObject。蘋果很有可能擁有這個名字的私人方法,或者將來會添加一個。

+0

嗨,肯,謝謝你的回覆。我更新了'+ keyPathsForValuesAffectingSelectedObject'來返回'[@「selectionIndexes」,@「selectionIndex」,@「arrangedObjects」],但這並沒有改變任何東西。表視圖的'selectionIndexes'被正確綁定到'NSArrayController'的'selectionIndexes'。感謝關於名稱的提示,這是一個很好的觀點。 – DanielGibbs

+0

如果您的'selectedObject'的觀察者也觀察到'selectionIndexes'和'selectionIndex',它是否會得到這些改變的通知? –

+0

是的,它的確如此。看來它是使用'keyPathsForValuesAffecting ...'指定的依賴項,它們不能與'NSArrayController'一起使用。創建我自己的子類並手動觀察值的作品。 – DanielGibbs