15

NSOutlineViewディレクトリ階層を表示している があります。これNSTreeControllerは、ファイル システム ノードを管理するクラスにバインドされています。ファイルシステム イベントが発生すると、キーパスで KVO 通知を起動しchildrenます。これにより、アウトライン ビューが更新されます。しかし、更新すると、突然一番上までスクロールします。スクロール位置を同じに保ちたい。何か案は?

FS イベントが発生したときに実行されるコードは次のとおりです。

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

これはモデル内にあるため、ビューにアクセスできません。

4

2 に答える 2

10

私は次のことをテストしたり試みたりしていませんが、とにかく試してみると思いました.

まず、NS*Controller を使用して NSTableView または NSOutlineView で複雑なものを管理するのは苦痛であり、簡潔さと引き換えに正確な制御を犠牲にします。このような状況で動作に問題がある場合は、独自のカスタム コントローラーにデータソースとデリゲート プロトコル (NSTableViewDataSource、NSTableViewDelegate または NSOutlineViewDataSource、NSOutlineViewDelegate) を実装することを検討してください。

第二に、KVO 通知の発行に関する Warren Burton のコメントは適切です。これは、とにかくそのコレクションを制御 (および監視) しているため、責任のあるコントローラー (NSTreeController) に変更について伝える必要があるためです。さらに言えば、NSTreeController の add/insert/remove メソッドを直接使用する必要があります。現在行っている方法 (無効にするたびに構造全体を叩き、後でリセットする) では、ツリー全体がリロードされます。コントローラーはそのコレクションを監視しているため、アウトライン ビューにそれ自体を更新するように指示しています。これにより、最初に空のアウトラインが表示され、その後、アウトラインのさらに展開されたバージョンが表示される可能性があります。これにより、ユーザーの展開状態が失われます。ツリーコントローラーを介したモデルにより、よりスマートで、

3 番目に、上記の 2 番目の点を利用して、NSTreeController をサブクラス化し、追加/挿入/削除メソッドをオーバーライドして次のことを行うことを検討できます。

  1. アウトライン ビューにその-visibleRect.
  2. super を呼び出して変更を開始します。
  3. アウトラインビューを に伝え-scrollRectToVisible:ます。

手順 3 の呼び出しをメイン スレッドでスケジュールすることにより、呼び出しを遅らせる必要がある場合があります (そのため、実行ループを介した現在のトリップの後に発生します)。または、別の方法として、ステップ 3 を可視の rect をどこかに保存し、NSOutlineViewDelegate-outlineView:didAdd/RemoveRowView:forRow:メソッドを実装してこのフラグをチェックし、Rect-scrollRectToVisible:がゼロでない場合はそこから呼び出します (スクロールを調整しようとしないように NSZeroRect にリセットすることを忘れないでください)アウトライン行が追加または削除されるたびに)。

不格好ですが、NSTreeController を維持できる妥当な (?) パスです。

4番目に、代わりに(そして私が行く方法)、NSTreeControllerを完全に削除し、NSOutlineViewDataSource(およびNSOutlineViewDelegate)プロトコルを独自のコントローラークラスに実装し、そのコントローラーにツリー構造への追加または削除を直接処理させることができます。その後、KVO のタイミングを気にする必要がないため、よりクリーンになります。ノードを追加するたびに、表示されている四角形を確認し、アウトライン ビューを更新してから、同じメソッド内でスクロールをすべて調整し、実行ループをトリップすることができます。

これがお役に立てば幸いです。:-)

于 2015-04-26T17:45:11.290 に答える
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];
于 2015-04-24T06:33:33.267 に答える