如何以编程方式将项目符号列表添加到 NSTextView

2024-03-23

这个问题可能听起来很奇怪,但我已经为此苦苦挣扎了几天。

我有一个 NSTextView,可以显示一些带有一些格式选项的文本。其中之一是能够打开/关闭所选内容或当前行的项目符号列表(最简单的列表)。

我知道 NSTextView 上有一个 orderFrontListPanel: 方法,它打开带有可用列表参数的窗口以供选择和编辑(就像在 TextView 中按菜单->格式->列表...时一样)。 我已经弄清楚并实现了手动添加项目符号,并且 NSTextView 似乎与它们一起工作almost正确。通过说almost我的意思是它保留了制表符位置,在“输入”时继续列表,等等。但是有一些小故障不适合我并且与标准实现不同。

我试图找到以编程方式设置列表的默认方法,就像通过“列表...”菜单完成的那样,但没有成功。

我寻求帮助,每一点信息都将不胜感激:)。

P.S.:我研究了 TextView 源代码,发现了很多有趣的内容,但没有迹象或线索如何以编程方式启用列表。

Update

仍在调查中。我发现当您将 orderFrontListPanel: 发送到 NSTextView 然后选择项目符号并按 Enter 键时,不会向 NSTextView 发送任何特殊消息。这意味着项目符号列表可以在此弹出面板内的某个位置构建并直接设置到 TextView 的文本容器...


以编程方式将项目符号列表添加到 NSTextView 的两种方法:

方法一:

以下链接引导我使用第一种方法,但它没有必要迂回,除非您想对项目符号使用一些特殊的非 Unicode 字形:

  • 在 NSTextView 中显示隐藏字符 https://stackoverflow.com/questions/300086/display-hidden-characters-in-nstextview
  • 如何绘制一个没有 unicode 表示的 NSGlyph? https://stackoverflow.com/questions/8472791/how-to-draw-one-nsglyph-that-hasnt-unicode-representation
  • appendBezierPathWithGlyph 在 [NSBezierPath currentPoint] 中失败 https://stackoverflow.com/questions/2984776/appendbezierpathwithglyph-fails-in-nsbezierpath-currentpoint

这需要:(1)一个子类布局管理器,用项目符号符号替换某些任意字符; (2) 带有firstLineHeadIndent的段落样式,一个比该缩进稍大的制表位,以及将两者结合起来的用于换行的headIndent。

布局管理器如下所示:

#import <Foundation/Foundation.h>

@interface TickerLayoutManager : NSLayoutManager {

// Might as well let this class hold all the fonts used by the progress ticker.
// That way they're all defined in one place, the init method.
NSFont *fontNormal;
NSFont *fontIndent; // smaller, for indented lines
NSFont *fontBold;

NSGlyph glyphBullet;
CGFloat fWidthGlyphPlusSpace;

}

@property (nonatomic, retain) NSFont *fontNormal;
@property (nonatomic, retain) NSFont *fontIndent; 
@property (nonatomic, retain) NSFont *fontBold;
@property NSGlyph glyphBullet;
@property CGFloat fWidthGlyphPlusSpace;

@end

#import "TickerLayoutManager.h"

@implementation TickerLayoutManager

@synthesize fontNormal;
@synthesize fontIndent; 
@synthesize fontBold;
@synthesize glyphBullet;
@synthesize fWidthGlyphPlusSpace;

- (id)init {
    self = [super init];
    if (self) {
        self.fontNormal = [NSFont fontWithName:@"Baskerville" size:14.0f];
        self.fontIndent = [NSFont fontWithName:@"Baskerville" size:12.0f];
        self.fontBold = [NSFont fontWithName:@"Baskerville Bold" size:14.0f];
        // Get the bullet glyph.
        self.glyphBullet = [self.fontIndent glyphWithName:@"bullet"];
        // To determine its point size, put it in a Bezier path and take its bounds.
        NSBezierPath *bezierPath = [NSBezierPath bezierPath];
        [bezierPath moveToPoint:NSMakePoint(0.0f, 0.0f)]; // prevents "No current point for line" exception
        [bezierPath appendBezierPathWithGlyph:self.glyphBullet inFont:self.fontIndent];
        NSRect rectGlyphOutline = [bezierPath bounds];
        // The bullet should be followed with a space, so get the combined size...
        NSSize sizeSpace = [@" " sizeWithAttributes:[NSDictionary dictionaryWithObject:self.fontIndent forKey:NSFontAttributeName]];
        self.fWidthGlyphPlusSpace = rectGlyphOutline.size.width + sizeSpace.width;
        // ...which is for some reason inexact. If this number is too low, your bulleted text will be thrown to the line below, so add some boost.
        self.fWidthGlyphPlusSpace *= 1.5; // 
    }

    return self;
}

- (void)drawGlyphsForGlyphRange:(NSRange)range 
                        atPoint:(NSPoint)origin {

    // The following prints only once, even though the textview's string is set 4 times, so this implementation is not too expensive.
    printf("\nCalling TickerLayoutManager's drawGlyphs method.");

    NSString *string = [[self textStorage] string];
    for (int i = range.location; i < range.length; i++) {
        // Replace all occurrences of the ">" char with the bullet glyph.
        if ([string characterAtIndex:i] == '>')
            [self replaceGlyphAtIndex:i withGlyph:self.glyphBullet];
    }

    [super drawGlyphsForGlyphRange:range atPoint:origin];
}

@end

将布局管理器分配给窗口/视图控制器的 awakeFromNib 中的文本视图,如下所示:

- (void) awakeFromNib {

    // regular setup...

    // Give the ticker display NSTextView its subclassed layout manager.
    TickerLayoutManager *newLayoutMgr = [[TickerLayoutManager alloc] init];
    NSTextContainer *textContainer = [self.txvProgressTicker textContainer];
    // Use "replaceLM" rather than "setLM," in order to keep shared relnshps intact. 
    [textContainer replaceLayoutManager:newLayoutMgr];
    [newLayoutMgr release];
    // (Note: It is possible that all text-displaying controls in this class’s window will share this text container, as they would a field editor (a textview), although the fact that the ticker display is itself a textview might isolate it. Apple's "Text System Overview" is not clear on this point.)

}

然后添加一个类似这样的方法:

- (void) addProgressTickerLine:(NSString *)string 
                   inStyle:(uint8_t)uiStyle {

    // Null check.
    if (!string)
        return;

    // Prepare the font.
    // (As noted above, TickerLayoutManager holds all 3 ticker display fonts.)
    NSFont *font = nil;
    TickerLayoutManager *tickerLayoutMgr = (TickerLayoutManager *)[self.txvProgressTicker layoutManager];
    switch (uiStyle) {
        case kTickerStyleNormal:
            font = tickerLayoutMgr.fontNormal;
            break;
        case kTickerStyleIndent:
            font = tickerLayoutMgr.fontIndent;
            break;
        case kTickerStyleBold:
            font = tickerLayoutMgr.fontBold;
            break;
        default:
            font = tickerLayoutMgr.fontNormal;
            break;
    }


    // Prepare the paragraph style, to govern indentation.    
    // CAUTION: If you propertize it for re-use, make sure you don't mutate it once it has been assigned to an attributed string. (See warning in class ref.)
    // At the same time, add the initial line break and, if indented, the tab.
    NSMutableParagraphStyle *paragStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; // ALLOC
    [paragStyle setAlignment:NSLeftTextAlignment]; // default, but just in case
    if (uiStyle == kTickerStyleIndent) {
        // (The custom layout mgr will replace ‘>’ char with a bullet, so it should be followed with an extra space.)
        string = [@"\n>\t" stringByAppendingString:string];
        // Indent the first line up to where the bullet should appear.
        [paragStyle setFirstLineHeadIndent:15.0f];
        // Define a tab stop to the right of the bullet glyph.
        NSTextTab *textTabFllwgBullet = [[NSTextTab alloc] initWithType:NSLeftTabStopType location:15.0f + tickerLayoutMgr.fWidthGlyphPlusSpace];
        [paragStyle setTabStops:[NSArray arrayWithObject:textTabFllwgBullet]];  
        [textTabFllwgBullet release];
        // Set the indentation for the wrapped lines to the same place as the tab stop.
        [paragStyle setHeadIndent:15.0f + tickerLayoutMgr.fWidthGlyphPlusSpace];
    }
    else {
        string = [@"\n" stringByAppendingString:string];
    }


    // PUT IT ALL TOGETHER.
    // Combine the above into a dictionary of attributes.
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                            font, NSFontAttributeName, 
                            paragStyle, NSParagraphStyleAttributeName, 
                            nil];
    // Use the attributes dictionary to make an attributed string out of the plain string.
    NSAttributedString *attrs = [[NSAttributedString alloc] initWithString:string attributes:dict]; // ALLOC
    // Append the attributed string to the ticker display.
    [[self.txvProgressTicker textStorage] appendAttributedString:attrs];

    // RELEASE
    [attrs release];
    [paragStyle release];

}

测试一下:

NSString *sTicker = NSLocalizedString(@"First normal line of ticker should wrap to left margin", @"First normal line of ticker should wrap to left margin");
[self addProgressTickerLine:sTicker inStyle:kTickerStyleNormal];
sTicker = NSLocalizedString(@"Indented ticker line should have bullet point and should wrap farther to right.", @"Indented ticker line should have bullet point and should wrap farther to right.");
[self addProgressTickerLine:sTicker inStyle:kTickerStyleIndent];
sTicker = NSLocalizedString(@"Try a second indented line, to make sure both line up.", @"Try a second indented line, to make sure both line up.");
[self addProgressTickerLine:sTicker inStyle:kTickerStyleIndent];
sTicker = NSLocalizedString(@"Final bold line", @"Final bold line");
[self addProgressTickerLine:sTicker inStyle:kTickerStyleBold];

你得到这个:

方法二:

但项目符号是一个常规的 Unicode 字符,十六进制为 2022。因此您可以直接将其放入字符串中,并获得精确的测量值,如下所示:

    NSString *stringWithGlyph = [NSString stringWithUTF8String:"\u2022"];
    NSString *stringWithGlyphPlusSpace = [stringWithGlyph stringByAppendingString:@" "];
    NSSize sizeGlyphPlusSpace = [stringWithGlyphPlusSpace sizeWithAttributes:[NSDictionary dictionaryWithObject:self.fontIndent forKey:NSFontAttributeName]];
    self.fWidthGlyphPlusSpace = sizeGlyphPlusSpace.width;

所以不需要自定义布局管理器。只需如上所述设置 paragStyle 缩进,并将文本字符串附加到包含行 return + 项目符号字符 + 空格(或 + tab,在这种情况下您仍然需要该制表位)的字符串。

使用空格,这会产生更紧凑的结果:

想要使用子弹以外的角色吗?这是一个漂亮的 Unicode 图表:http://www.danshort.com/unicode/ http://www.danshort.com/unicode/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何以编程方式将项目符号列表添加到 NSTextView 的相关文章

  • 观察 UIDatePicker 的变化

    我注意到没有委托来观察 UIDatePicker 中的变化 有没有一种方法可以在不确认任何内容的情况下检测选择器中何时进行更改 例如它旋转并落在新数字上的那一刻 我希望能够检测到这一点 我考虑过关键值观察 但我不认为有一个属性会立即改变 您
  • xcode 4 垃圾收集已删除?

    我正在学习 Xcode 和 Objective C 但是我正在阅读的书使用的是旧版本 3 2 的 Xcode 其中一章完全致力于内存和泄漏 说我需要通过 项目设置 gt 构建 打开垃圾收集 但是 我找不到垃圾收集设置 它已被删除还是我只是错
  • 如何连续关闭 2 个模态视图控制器?

    我有 2 个以模态方式呈现的视图控制器 A presents B which presents C 当我解雇C时 我也想解雇B 但我不知道该怎么做 解雇C self dismissModalViewControllerAnimated YE
  • 对 numpy 数组中存在的元组中的列进行切片

    我已将一个文本文件导入到 numpy 数组中 如下所示 data np genfromtxt f dtype None delimiter names None 其中 f 包含我的 csv 文件的路径 现在数据包含以下内容 array 53
  • Objective-C - NSNotificationCenter 放在哪里?

    我有一个 NSNotificationCenter 选择器 把它放在哪里 在委托中 如果是 那么在哪里 在控制器中 方法也放在哪里 我需要解除分配 NSNotificationCenter 吗 NSNotificationCenter de
  • 在 OS X 中,如何检测当前活动应用程序何时发生变化?

    尝试为 OS X 编写一个应用程序 该应用程序根据当前的应用程序触发行为 它不需要与它交互 它只需要知道何时更改以及更改内容 任何人都可以推荐哪些 API 可用于此目的吗 我的猜测是辅助服务中的某些内容 因为这是大多数应用程序 例如 Bet
  • NSView 鼠标跟踪

    我在 Mac OS X 上遇到了 Cocoa NSView 的奇怪行为 我在 NSView 容器中有一个自定义 NSView 这个自定义 NSView 跟踪鼠标移动 点击 并有一个工具提示 当我在所描述的视图上方添加 NSView 时 即使
  • 通过递归扩展 Prolog 目标?

    我 最终 实现了一些目标 这些目标将根据开始由 开始之后 and duration 然而 计划目标仅接受规定数量的任务 我想扩展计划目标的功能以接受单个列表并在计划时迭代该列表 不幸的是 我认为这将需要与can run and 冲突目标如下
  • 停止 ARC 在空闲时释放对象

    我在 Objective C 中遇到内存管理问题 我一直在阅读高级内存管理编程指南 https developer apple com library mac documentation Cocoa Conceptual MemoryMgm
  • 如何让 Chrome Cast 在 iOS 后台运行?

    我有一个简单的问题 当您进入 iPhone 的主屏幕并且不退出应用程序时 chrome Cast 设备会停止在屏幕上播放视频 当应用程序在后台运行时 我如何才能保持播放状态 如果您有一个视频应用程序并且它在投射设备中运行 您可能需要以下行为
  • 从哪里开始使用适用于 Retina 显示屏的 OpenGL 绘制程序

    我知道由于这里提到的错误 我无法将 GLPainter 示例从苹果适应到视网膜 在 Retina iPad 上显示全屏 CAEAGLLayer 时出现问题 https stackoverflow com questions 9757052
  • sizeToFit 运行异常

    我有一段代码 每次发生后端数据库更改时都会执行 本质上我在父视图中有一个标签 标签由许多状态消息之一更新 每个状态消息位于不同的行上 并以换行符 n 结尾 每条状态消息只能在一行上 并且不能过多 我遇到的问题是 当视图首次重新加载时 一切正
  • 挑战:优化取消列出[简单]

    因为 SO 最近有点慢 所以我发布了一个简单的问题 如果大鱼们能在这场比赛中留在替补席上并给新秀们一个回应的机会 我将不胜感激 有时我们的对象具有大量的大列表元素 向量 您如何将这个对象 取消列出 到单个向量中 证明你的方法比unlist
  • 在后台运行 URL 请求

    我想在一定的时间间隔内发出 url 请求 例如 每 10 分钟应用程序应该发出一次 url 调用并获取一些 json 数据 应用程序在后台运行时应该能够执行此操作 这可以做到吗 如果是这样 这是否违反 Apple 服务条款 有什么限制吗 i
  • UIImageWriteToSavedPhotosAlbum 选择器语法问题

    努力让 UIImageWriteToSavedPhotosAlbum 快速工作https developer apple com library ios documentation UIKit Reference UIKitFunction
  • 如何使用foldr为列表创建显示实例?

    我想为我的数据类型 我的列表 编写自己的显示实例 到目前为止 我的方法是有效的 但我总是在末尾有一个逗号 我已经尝试用最后一个元素启动折叠并将其从列表中删除 但它很麻烦而且不起作用 有没有更简单的方法来获得正确的解决方案 实际 1 2 3
  • 如何使用 MonoTouch 在 UIKeyboard 之上添加 UIToolbar?

    我按照 Obj C 中的示例进行操作定制 iPhone 键盘 https stackoverflow com questions 1610542 custom iphone keyboard 1612549 1612549但是 为了在 Wi
  • 如何在没有 MFMessageComposeViewController 的情况下发送和接收短信?

    我想发送和接收短信而不显示MFMessageViewController从我的申请中 有人能告诉我这怎么可能吗 不可能 除非您使用第 3 方 api 发送 接收短信
  • 共享扩展程序未出现在能够在 iPhone 上共享照片的应用程序列表中

    我正在尝试创建一个共享应用程序扩展 并按照以下来源的教程进行操作 http www technetexperts com mobile share extension in ios application overview with exa
  • GCD 与自定义队列

    我想知道这两者的性能有什么区别 dispatch async dispatch get global queue DISPATCH QUEUE PRIORITY HIGH 0 perform complex operation dispat

随机推荐