2013-03-21 12 views
5

OS X向けの小さなサーバーアプリケーションを作成しています。接続されたクライアントに関する情報を記録するためにNSTextViewを使用しています。 NSTextViewを下にスクロールする

私は何か、私はNSTextViewのテキストに、このように新しいメッセージを追加していますログインする必要がありたび

- (void)logMessage:(NSString *)message 
{ 
    if (message) { 
     self.textView.string = [self.textView.string stringByAppendingFormat:@"%@\n",message]; 
    } 
} 

私はNSTextFieldをたいと思い、この後に(または多分私はNSClipViewを言う必要がありますが、そのそのテキストの最後の行を表示するようにスクロールダウンします(明らかに、最後の行がまだ表示されていない場合にのみスクロールする必要があります。実際には、新しい行が最初の行であれば、下にスクロールする必要はありません)。

これをプログラムで行うにはどうすればよいですか?

答えて

11

見つかりソリューション:

- (void)logMessage:(NSString *)message 
{ 
    if (message) { 
     [self appendMessage:message]; 
    } 
} 

- (void)appendMessage:(NSString *)message 
{ 
    NSString *messageWithNewLine = [message stringByAppendingString:@"\n"]; 

    // Smart Scrolling 
    BOOL scroll = (NSMaxY(self.textView.visibleRect) == NSMaxY(self.textView.bounds)); 

    // Append string to textview 
    [self.textView.textStorage appendAttributedString:[[NSAttributedString alloc]initWithString:messageWithNewLine]]; 

    if (scroll) // Scroll to end of the textview contents 
     [self.textView scrollRangeToVisible: NSMakeRange(self.textView.string.length, 0)]; 
} 
+0

[OK]を、私は「スマート」BOOLスクロールの必要性が表示されません。まず、BOOLスクロールの式では、演算子は==の代わりに!=にする必要があります。意味があり、!=私のために働くが、==は機能しません。次に、改行で終わる1行のテキストを追加すると、改行が表示されることがあり、時には改行がないこともあります。なぜ私たちが「テキストビューの内容の終わりまでスクロールしたくない」のではないのか分かりません。それが私たちが望むものです。すべての場合。私はif(スクロール)行を削除し、うまく動作します。たぶん我々は反対側のケースでテストしています:)) –

+1

「[self.textView scrollRangeToVisible:NSMakeRange(self.textView.string.length、0)]」を使用するように注意してください。これは実際にはNSTextViewのレイアウトに応じて、実際には下にスクロールしないことがあります。)NSTextViewの高さがテキスト行の高さで均等に割り切れない場合、テキストの下の行を部分的に切り捨てる可能性があります(この場合、スマートスクロールは機能しません)。 : "self.textView.scrollToVisible(NSRect(x:0、y:self.textView.frame.height-1、width:self.textView.frame.width、height:1))"。 – pauln

+0

また、私はスクロールフラグを微調整していることが分かりました。ちょっとしたスラックがありますので、正確な一番下にある必要はありません(Swiftの例): 'let scroll = abs(self.logTextView.visibleRect。 maxY - self.logTextView.bounds.maxY) pauln

1

私はそれを確実に動作させることができなかったので、私は、しばらくの間、このをいじってきました。私は最終的に私のコードが動作したので、私は返信として投稿したいと思います。

私のソリューションでは、出力がビューに追加されている間に手動でスクロールすることができます。 NSTextViewの絶対下部までスクロールすると、自動的にスクロールが再開されます(有効になっている場合)。まず

この必要なだけ...

FSScrollToBottomExtensions.h #IMPORTするカテゴリ:

@interface NSView (FSScrollToBottomExtensions) 
- (float)distanceToBottom; 
- (BOOL)isAtBottom; 
- (void)scrollToBottom; 
@end 

がFSScrollToBottomExtensions.m:

@implementation NSView (FSScrollToBottomExtensions) 
- (float)distanceToBottom 
{ 
    NSRect visRect; 
    NSRect boundsRect; 

    visRect = [self visibleRect]; 
    boundsRect = [self bounds]; 
    return(NSMaxY(visRect) - NSMaxY(boundsRect)); 
} 

// Apple's suggestion did not work for me. 
- (BOOL)isAtBottom 
{ 
    return([self distanceToBottom] == 0.0); 
} 

// The scrollToBottom method provided by Apple seems unreliable, so I wrote this one 
- (void)scrollToBottom 
{ 
    NSPoint  pt; 
    id   scrollView; 
    id   clipView; 

    pt.x = 0; 
    pt.y = 100000000000.0; 

    scrollView = [self enclosingScrollView]; 
    clipView = [scrollView contentView]; 

    pt = [clipView constrainScrollPoint:pt]; 
    [clipView scrollToPoint:pt]; 
    [scrollView reflectScrolledClipView:clipView]; 
} 
@end 

を... "自分で作成することOutputView "(NSTextViewのサブクラス):

FSOutputView.h:

@interface FSOutputView : NSTextView 
{ 
    BOOL    scrollToBottomPending; 
} 

FSOutputView.m:

@implementation FSOutputView 

- (id)setup 
{ 
    ... 
    return(self); 
} 

- (id)initWithCoder:(NSCoder *)aCoder 
{ 
    return([[super initWithCoder:aCoder] setup]); 
} 

- (id)initWithFrame:(NSRect)aFrame textContainer:(NSTextContainer *)aTextContainer 
{ 
    return([[super initWithFrame:aFrame textContainer:aTextContainer] setup]); 
} 

- (void)dealloc 
{ 
    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
    [super dealloc]; 
} 

- (void)awakeFromNib 
{ 
    NSNotificationCenter *notificationCenter; 
    NSView     *view; 

    // viewBoundsDidChange catches scrolling that happens when the caret 
    // moves, and scrolling caused by pressing the scrollbar arrows. 
    view = [self superview]; 
    [notificationCenter addObserver:self 
    selector:@selector(viewBoundsDidChangeNotification:) 
     name:NSViewBoundsDidChangeNotification object:view]; 
    [view setPostsBoundsChangedNotifications:YES]; 

    // viewFrameDidChange catches scrolling that happens because text 
    // is inserted or deleted. 
    // it also catches situations, where window resizing causes changes. 
    [notificationCenter addObserver:self 
     selector:@selector(viewFrameDidChangeNotification:) 
     name:NSViewFrameDidChangeNotification object:self]; 
    [self setPostsFrameChangedNotifications:YES]; 

} 

- (void)handleScrollToBottom 
{ 
    if(scrollToBottomPending) 
    { 
     scrollToBottomPending = NO; 
     [self scrollToBottom]; 
    } 
} 

- (void)viewBoundsDidChangeNotification:(NSNotification *)aNotification 
{ 
    [self handleScrollToBottom]; 
} 

- (void)viewFrameDidChangeNotification:(NSNotification *)aNotification 
{ 
    [self handleScrollToBottom]; 
} 

- (void)outputAttributedString:(NSAttributedString *)aAttributedString 
    flags:(int)aFlags 
{ 
    NSRange      range; 
    BOOL      wasAtBottom; 

    if(aAttributedString) 
    { 
     wasAtBottom = [self isAtBottom]; 

     range = [self selectedRange]; 
     if(aFlags & FSAppendString) 
     { 
      range = NSMakeRange([[self textStorage] length], 0); 
     } 
     if([self shouldChangeTextInRange:range 
      replacementString:[aAttributedString string]]) 
     { 
      [[self textStorage] beginEditing]; 
      [[self textStorage] replaceCharactersInRange:range 
       withAttributedString:aAttributedString]; 
      [[self textStorage] endEditing]; 
     } 

     range.location += [aAttributedString length]; 
     range.length = 0; 
     if(!(aFlags & FSAppendString)) 
     { 
      [self setSelectedRange:range]; 
     } 

     if(wasAtBottom || (aFlags & FSForceScroll)) 
     { 
      scrollToBottomPending = YES; 
     } 
    } 
} 
@end 

...あなたは(私はそれを取り除いてきました)このクラスにさらにいくつかの便利なメソッドを追加することができ、フォーマットされた文字列を出力することができますように。

- (void)outputString:(NSString *)aFormatString arguments:(va_list)aArguments attributeKey:(NSString *)aKey flags:(int)aFlags 
{ 
    NSMutableAttributedString *str; 

    str = [... generate attributed string from parameters ...]; 
    [self outputAttributedString:str flags:aFlags]; 
} 

- (void)outputLineWithFormat:(NSString *)aFormatString, ... 
{ 
    va_list   args; 
    va_start(args, aFormatString); 
    [self outputString:aFormatString arguments:args attributeKey:NULL flags:FSAddNewLine]; 
    va_end(args); 
} 
5

OS 10.6では、nsTextView.scrollToEndOfDocument(self)と同じくらい簡単です。

+0

ありがとう!また、自己の代わりにnilを渡すこともできます。 –

+0

これは実際にはうまくいくと私は同意しますが、このNSResponderメソッドがNSTextViewによって正式に実装され(サポートされている)、Appleの文書にはこのNSResponderメソッドの参照が見つかりません。リンクをお願いしますか? – pauln

+0

私は2日間このことを混乱させていたとは信じられません....ありがとうございました! – BadgerBadger

0

私のオプションを使用することでしたので、私はいくつかのカスタマイズされたNSTextViewとカスタム入力方法があります:

self.scrollView.contentView.scroll(NSPoint(x: 1, y: self.textView.frame.size.height)) 
関連する問題