我正在为OS X的一个小服务器应用程序,我使用的是NSTextView记录有关连接的客户端的一些信息。
每当我需要登录的东西,我追加新的消息到NSTextView这样的文字:
- (void)logMessage:(NSString *)message
{
if (message) {
self.textView.string = [self.textView.string stringByAppendingFormat:@"%@\n",message];
}
}
在此之后,我想了的NSTextField(或者我应该说,包含它的NSClipView)向下滚动,以显示其文本的最后一行(显然它应该滚动只有当最后一行是不可见的是,其实如果当时新的生产线是我记录它已经在屏幕上,所以没有需要向下滚动)的第一行。
我怎样才能做到这一点编程?
找到解决方案:
- (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)];
}
作为OS 10.6的它的那样简单nsTextView.scrollToEndOfDocument(self)
。
我一直在搞乱这一段时间,因为我无法得到它可靠地工作。 我终于得到了我的代码工作,所以我想将它张贴作为回复。
我的解决方案允许用户手动滚动,而输出被添加到视图。 只要你滚动到NSTextView的绝对底部,自动滚动将恢复(如果启用,这是)。
首先需要当一个类别只#IMPORT这...
FSScrollToBottomExtensions.h:
@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);
}
我有些定制NSTextView和自定义输入法,所以我的选择是使用方法:
self.scrollView.contentView.scroll(NSPoint(x: 1, y: self.textView.frame.size.height))
斯威夫特4
let smartScroll = self.textView.visibleRect.maxY == self.textView.bounds.maxY
self.textView.textStorage?.append("new text")
if smartScroll{
self.textView.scrollToEndOfDocument(self)
}