Mac开发基础14-NSTextView(二)

进阶使用和技巧

1. 扩展查找和替换功能

可以自定义查找和替换功能,包括高亮查找结果、批量替换等。

查找并高亮

Objective-C
- (void)highlightOccurrencesOfString:(NSString *)searchString {
    // 清除之前的高亮效果
    [textView.layoutManager removeTemporaryAttribute:NSBackgroundColorAttributeName forCharacterRange:NSMakeRange(0, textView.string.length)];
    
    // 查找并高亮指定字符串
    NSString *textViewString = textView.string;
    NSRange searchRange = NSMakeRange(0, textViewString.length);
    while (searchRange.location < textViewString.length) {
        searchRange = [textViewString rangeOfString:searchString options:0 range:searchRange];
        if (searchRange.location != NSNotFound) {
            // 应用高亮颜色
            [textView.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] forCharacterRange:searchRange];
            searchRange.location += searchRange.length;
            searchRange.length = textViewString.length - searchRange.location;
        } else {
            break;
        }
    }
}
Swift
func highlightOccurrences(of searchString: String) {
    // 清除之前的高亮效果
    textView.layoutManager?.removeTemporaryAttribute(.backgroundColor, forCharacterRange: NSRange(location: 0, length: textView.string.utf16.count))
    
    // 查找并高亮指定字符串
    let textViewString = textView.string as NSString
    var searchRange = NSRange(location: 0, length: textViewString.length)
    while let foundRange = textViewString.range(of: searchString, options: [], range: searchRange).toRange() {
        // 应用高亮颜色
        textView.layoutManager?.addTemporaryAttribute(.backgroundColor, value: NSColor.yellow, forCharacterRange: NSRange(foundRange))
        searchRange = NSRange(location: foundRange.upperBound, length: textViewString.length - foundRange.upperBound)
    }
}

2. 自动链接检测

NSTextView 具有自动检测和格式化 URL 的功能,可以自动将 URL 链接转换为可点击的超链接。

启用自动链接检测

Objective-C
textView.automaticLinkDetectionEnabled = YES;
Swift
textView.isAutomaticLinkDetectionEnabled = true

3. 自定义上下文菜单

可以为 NSTextView 提供自定义的上下文菜单,以增加更多操作选项。

Objective-C
- (NSMenu *)menuForEvent:(NSEvent *)event {
    NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Context Menu"];
    [menu addItemWithTitle:@"Custom Action" action:@selector(customAction:) keyEquivalent:@""];
    [menu addItemWithTitle:@"Another Action" action:@selector(anotherAction:) keyEquivalent:@""];
    return menu;
}

- (void)customAction:(id)sender {
    NSLog(@"Custom action triggered");
}

- (void)anotherAction:(id)sender {
    NSLog(@"Another action triggered");
}
Swift
override func menu(for event: NSEvent) -> NSMenu? {
    let menu = NSMenu(title: "Context Menu")
    menu.addItem(withTitle: "Custom Action", action: #selector(customAction(_:)), keyEquivalent: "")
    menu.addItem(withTitle: "Another Action", action: #selector(anotherAction(_:)), keyEquivalent: "")
    return menu
}

@objc func customAction(_ sender: Any?) {
    print("Custom action triggered")
}

@objc func anotherAction(_ sender: Any?) {
    print("Another action triggered")
}

4. 自定义文本标记(标注)

可以利用 NSTextViewNSLayoutManagerNSTextStorage 结合 NSAttachment 添加自定义的文本标记或标注。

Objective-C
- (void)addCustomMarkAtRange:(NSRange)range {
    // 创建自定义附件标注
    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];
    [textView.textStorage insertAttributedString:attachmentString atIndex:range.location];
    
    // 添加自定义属性
    [textView.textStorage addAttributes:@{NSForegroundColorAttributeName: [NSColor redColor]} range:range];
}
Swift
func addCustomMark(at range: NSRange) {
    // 创建自定义附件标注
    let attachment = NSTextAttachment()
    let attachmentString = NSAttributedString(attachment: attachment)
    textView.textStorage?.insert(attachmentString, at: range.location)
    
    // 添加自定义属性
    textView.textStorage?.addAttributes([.foregroundColor: NSColor.red], range: range)
}

5. 处理空格键和回车键

可以自定义处理空格键和回车键,实现更灵活的输入行为。

Objective-C
- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
    if (commandSelector == @selector(insertNewline:)) {
        // 自定义处理回车键
        NSLog(@"Return key pressed");
        return YES;
    } else if (commandSelector == @selector(insertTab:)) {
        // 自定义处理Tab键
        NSLog(@"Tab key pressed");
        return YES;
    }
    return NO;
}
Swift
override func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
    if commandSelector == #selector(insertNewline(_:)) {
        // 自定义处理回车键
        print("Return key pressed")
        return true
    } else if commandSelector == #selector(insertTab(_:)) {
        // 自定义处理Tab键
        print("Tab key pressed")
        return true
    }
    return false
}

常用工具类封装

下面,我们将封装一个 NSTextView 的工具类,以更灵活地定制 NSTextView 的行为和功能。

Objective-C

@interface NSTextViewHelper : NSObject

@property (nonatomic, strong) NSTextView *textView;

- (instancetype)initWithTextView:(NSTextView *)textView;

- (void)setPlaceholder:(NSString *)placeholder;
- (void)setTextColor:(NSColor *)color;
- (void)setFont:(NSFont *)font;
- (void)setEditable:(BOOL)editable;
- (void)setSelectable:(BOOL)selectable;
- (void)highlightOccurrencesOfString:(NSString *)searchString;
- (void)addCustomMarkAtRange:(NSRange)range;
- (void)setCustomContextMenu;

@end

@implementation NSTextViewHelper

- (instancetype)initWithTextView:(NSTextView *)textView {
    self = [super init];
    if (self) {
        _textView = textView;
    }
    return self;
}

- (void)setPlaceholder:(NSString *)placeholder {
    if ([_textView isKindOfClass:[NSTextView class]]) {
        // 当前没有直接设置占位符的方法,可以通过扩展创建占位符效果
    }
}

- (void)setTextColor:(NSColor *)color {
    [_textView setTextColor:color];
}

- (void)setFont:(NSFont *)font {
    [_textView setFont:font];
}

- (void)setEditable:(BOOL)editable {
    [_textView setEditable:editable];
}

- (void)setSelectable:(BOOL)selectable {
    [_textView setSelectable:selectable];
}

- (void)highlightOccurrencesOfString:(NSString *)searchString {
    // 清除之前的高亮效果
    [_textView.layoutManager removeTemporaryAttribute:NSBackgroundColorAttributeName forCharacterRange:NSMakeRange(0, _textView.string.length)];
    
    // 查找并高亮指定字符串
    NSString *textViewString = _textView.string;
    NSRange searchRange = NSMakeRange(0, textViewString.length);
    while (searchRange.location < textViewString.length) {
        searchRange = [textViewString rangeOfString:searchString options:0 range:searchRange];
        if (searchRange.location != NSNotFound) {
            // 应用高亮颜色
            [_textView.layoutManager addTemporaryAttribute:NSBackgroundColorAttributeName value:[NSColor yellowColor] forCharacterRange:searchRange];
            searchRange.location += searchRange.length;
            searchRange.length = textViewString.length - searchRange.location;
        } else {
            break;
        }
    }
}

- (void)addCustomMarkAtRange:(NSRange)range {
    // 创建自定义附件标注
    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];
    [_textView.textStorage insertAttributedString:attachmentString atIndex:range.location];
    
    // 添加自定义属性
    [_textView.textStorage addAttributes:@{NSForegroundColorAttributeName: [NSColor redColor]} range:range];
}

- (void)setCustomContextMenu {
    [_textView setMenu:[self customMenu]];
}

- (NSMenu *)customMenu {
    NSMenu *menu = [[NSMenu alloc] initWithTitle:@"ContextMenu"];
    [menu addItemWithTitle:@"Custom Action" action:@selector(customAction:) keyEquivalent:@""];
    [menu addItemWithTitle:@"Another Action" action:@selector(anotherAction:) keyEquivalent:@""];
    return menu;
}

- (void)customAction:(id)sender {
    NSLog(@"Custom action triggered");
}

- (void)anotherAction:(id)sender {
    NSLog(@"Another action triggered");
}

@end

Swift

class NSTextViewHelper {

    private weak var textView: NSTextView?

    init(textView: NSTextView) {
        self.textView = textView
    }

    func setPlaceholder(_ placeholder: String) {
        if let textView = textView {
            // 当前没有直接设置占位符的方法,可以通过扩展创建占位符效果
        }
    }

    func setTextColor(_ color: NSColor) {
        textView?.textColor = color
    }

    func setFont(_ font: NSFont) {
        textView?.font = font
    }

    func setEditable(_ editable: Bool) {
        textView?.isEditable = editable
    }

    func setSelectable(_ selectable: Bool) {
        textView?.isSelectable = selectable
    }

    func highlightOccurrences(of searchString: String) {
        guard let textView = textView else { return }
        
        // 清除之前的高亮效果
        textView.layoutManager?.removeTemporaryAttribute(.backgroundColor, forCharacterRange: NSRange(location: 0, length: textView.string.utf16.count))
        
        // 查找并高亮指定字符串
        let textViewString = textView.string as NSString
        var searchRange = NSRange(location: 0, length: textViewString.length)
        while let foundRange = textViewString.range(of: searchString, options: [], range: searchRange).toRange() {
            // 应用高亮颜色
            textView.layoutManager?.addTemporaryAttribute(.backgroundColor, value: NSColor.yellow, forCharacterRange: NSRange(foundRange))
            searchRange = NSRange(location: foundRange.upperBound, length: textViewString.length - foundRange.upperBound)
        }
    }

    func addCustomMark(at range: NSRange) {
        guard let textView = textView else { return }
        
        // 创建自定义附件标注
        let attachment = NSTextAttachment()
        let attachmentString = NSAttributedString(attachment: attachment)
        textView.textStorage?.insert(attachmentString, at: range.location)
        
        // 添加自定义属性
        textView.textStorage?.addAttributes([.foregroundColor: NSColor.red], range: range)
    }

    func setCustomContextMenu() {
        textView?.menu = customMenu()
    }

    private func customMenu() -> NSMenu {
        let menu = NSMenu(title: "ContextMenu")
        menu.addItem(withTitle: "Custom Action", action: #selector(customAction(_:)), keyEquivalent: "")
        menu.addItem(withTitle: "Another Action", action: #selector(anotherAction(_:)), keyEquivalent: "")
        return menu
    }

    @objc func customAction(_ sender: Any?) {
        print("Custom action triggered")
    }

    @objc func anotherAction(_ sender: Any?) {
        print("Another action triggered")
    }
}

使用示例

Objective-C

NSTextView *textView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 400, 300)];
NSTextViewHelper *helper = [[NSTextViewHelper alloc] initWithTextView:textView];
[helper setTextColor:[NSColor blueColor]];
[helper setFont:[NSFont fontWithName:@"Helvetica" size:14]];
[helper setEditable:YES];
[helper highlightOccurrencesOfString:@"NSTextView"];
[helper addCustomMarkAtRange:NSMakeRange(0, 5)];
[helper setCustomContextMenu];

Swift

let textView = NSTextView(frame: NSMakeRect(0, 0, 400, 300))
let helper = NSTextViewHelper(textView: textView)
helper.setTextColor(.blue)
helper.setFont(NSFont(name: "Helvetica", size: 14)!)
helper.setEditable(true)
helper.highlightOccurrences(of: "NSTextView")
helper.addCustomMark(at: NSRange(location: 0, length: 5))
helper.setCustomContextMenu()
posted @ 2024-08-06 16:24  Mr.陳  阅读(13)  评论(0编辑  收藏  举报