2015-04-20 32 views
15

我有一個顯示目錄層次結構的NSOutlineView。它綁定到一個NSTreeController,它綁定到我的類管理文件系統節點。發生文件系統事件時,我會在children keypath上觸發KVO通知,這會導致大綱視圖更新。但是當它更新時,它會突然滾動到最頂端。我希望滾動位置保持不變。有任何想法嗎?當內容更新時,NSOutlineView跳轉到頂部

下面是當FS事件發生時運行的代碼:

- (void)URLWatcher:(CDEvents *)URLWatcher eventOccurred:(CDEvent *)event { 
    [self willChangeValueForKey:@"children"]; 
    children = nil; // this will refreshed next time children is called 
    [self didChangeValueForKey:@"children"]; 
} 

這是在模型中,所以我不能訪問視圖。

+0

您是重新加載整個大綱視圖還是僅需要更新的特定項目? – rocky

+0

@rocky我沒有明確重新加載大綱視圖。樹控制器爲我重新加載它。 – tbodt

+0

什麼對象得到這個消息?相關的FS節點表示還是控制器?對於我爲什麼要在這裏發射KVO有點不清楚。另外你怎麼刷新孩子。你能證明這一點嗎?我構建了一個快速樹控制器/ nsoutlineview,只要我不開啓KVO,我不會在更新時重置視圖。 –

回答

0

我建議你保持當前的滾動視圖偏移量,並在發送KVO通知並更新大綱視圖後恢復它。

- (void)updateOutlineView:(NSOutlineView *)outlineView 
{ 
    // first save offset 
    NSScrollView *scrollView = [outlineView enclosingScrollView]; 
    NSClipView *clipView = [scrollView contentView]; 
    NSPoint offset = clipView.bounds.origin; 
    // send KVO notification of the 'children' keypath 
    // ... 
    // restore offset 
    [clipView scrollPoint:offset]; 
} 

看看滾動點是絕對值。您可以根據更新的大綱視圖高度來計算目的地點。

//... before the notification sent 
CGFloat height = [[[[scrollView documentView] frame] size] height]; 
CGFloat yValue = offset.y/height; 
//... after the outline view updated 
CGFloat newHeight = [[[[scrollView documentView] frame] size] height]; 
offset.y = newHeight * yValue; 
[clipView scrollPoint:offset]; 
+0

這不適用於我的情況。爲了澄清,我已經將相關代碼添加到了我的問題中。 – tbodt

9

我還沒有測試或嘗試以下,但認爲我會給它一個鏡頭無論如何。首先,用NS *控制器管理任何與NSTableView或NSOutlineView複雜的事情是痛苦的,並犧牲精確控制以換取簡單。如果您發現自己在這種情況下發生了對抗行爲,請考慮在您自己的自定義控制器中實施數據源和委託協議(NSTableViewDataSource,NSTableViewDelegate或NSOutlineViewDataSource,NSOutlineViewDelegate)。第二,Warren Burton關於發射KVO通知的評論是有意義的,因爲你應該告訴負責控制器(你的NSTreeController)有關這些改變,因爲它是控制(和觀察)該集合的人。更重要的是,你應該直接使用NSTreeController的add/insert/remove方法。你現在這樣做的方式(每當你取消時重新打開整個結構然後重新設置)將導致整個樹重新加載。由於控制器正在觀察該集合,因此它會告訴大綱視圖自行刷新,可能會首先看到一個空的輪廓,然後是大綱的進一步擴展版本,這將會失去用戶的擴展狀態等。修改模型通過樹控制器將允許更智能,更高效的視圖更新。

第三,可以考慮通過繼承NSTreeController並重寫添加/插入取我的第二個點的優勢之上/刪除方法來做到以下幾點:

  1. 詢問外形上看其-visibleRect
  2. 致電超級啓動更改。
  3. 告訴大綱視圖到-scrollRectToVisible:

您可能不得不通過在主線程上調度它來延遲第3步的調用(所以它在當前通過運行循環之後發生)。或者,或者替換第3步,將可見矩形存儲在某個地方,然後執行NSOutlineViewDelegate方法檢查此標誌,如果rect非零,則調用-scrollRectToVisible:(請記住將其重置爲NSZeroRect,以便它不嘗試每次添加或刪除大綱行時調整滾動)。

笨重的,但一個合理的(?)路徑,讓您保持NSTreeController。第四,或者(和我想去的方式),你可以完全刪除NSTreeController並在你自己的控制器類中實現NSOutlineViewDataSource(和NSOutlineViewDelegate)協議,並讓該控制器直接處理添加到樹結構或從樹結構中刪除。然後它變得更清潔,因爲您不必擔心KVO的時間。在添加任何節點時,您可以注意到可見矩形,更新大綱視圖,然後在同一個方法中調整全部滾動,並在運行循環中跳轉。

我希望這可以幫助,而不是太漫無邊際。 :-)

+0

最後,一個答案! – tbodt

+0

我決定嘗試第四種選擇。可能會獎勵賞金。 – tbodt