swift - macOS:从 CharacterPalette 获取表情符号(修订版)

标签 swift macos

(这是一个修改后的问题 - 包括答案 - 继 macOS: Take emoji from characterPalette 之后更详细地描述了遇到的问题)

背景/用例

我有一个应用程序,我没有创建和维护图标库,而是让用户键入一个表情符号作为占位符图形。这在我的应用程序上下文中工作得很好,但我对我使用的输入机制不满意。

问题

我想简化这个,所以我打开 characterPalette,选择一个表情符号,然后将其显示为按钮的 StringValue 或标签(=不可编辑的 NSTextField)。

这似乎不可能。与 NSColorPanel 或 NSFontPanel 不同,characterPanel 没有暴露给 Cocoa 框架,所以我不能获取它的 selectedValue,设置它的 Action ,或者捕捉通知。 orderFrontCharacterPalette 的文档只是说打开字符调色板 ... 没有帮助。

尝试过的解决方案和遇到的问题

我尝试让我的接收器成为 firstResponder,但与 NSTextView 不同,NSTextField 无法处理表情符号。我找到了一个变通方法,使用前面带有 NSBox 的 NSTextView,使其成为 firstResponder,并使用 NSApp.orderFrontCharacterPalette(sender),但发现在各种情况下似乎都涉及额外的绘图调用 – 设置按钮的标题,显示 SystemFont Mini 大小的标签(常规大小工作正常) CharacterPalette 将打开(=系统菜单现在提供“隐藏表情符号和符号”)而不显示。 (这一直持续到应用程序关闭,即使您尝试通过常规菜单/快捷方式打开 CharacterPalette)

对于涉及 NSTextInputClient 的部分解决方案(不显示似乎是一个持久性错误),请参见下面的答案。

最佳答案

表情符号选择器需要 NSTextInputClient 的最小实现。例如一个按钮:

class MyButton: NSButton, NSTextInputClient {
    
    override var acceptsFirstResponder: Bool {
        get {
            return true
        }
    }
    
    override func becomeFirstResponder() -> Bool {
        return true
    }
    
    override func resignFirstResponder() -> Bool {
        return true
    }

    func insertText(_ string: Any, replacementRange: NSRange) {
        // this method is called when the user selects an emoji
        if let string = string as? String {
            self.title = string
        }
    }
    
    func setMarkedText(_ string: Any, selectedRange: NSRange, replacementRange: NSRange) {
    }
    
    func unmarkText() {
    }
    
    func selectedRange() -> NSRange {
        return NSMakeRange(0, 0)
    }
    
    func markedRange() -> NSRange {
        return NSMakeRange(NSNotFound, 0)
    }
    
    func hasMarkedText() -> Bool {
        return false
    }
    
    func attributedSubstring(forProposedRange range: NSRange, actualRange: NSRangePointer?) -> NSAttributedString? {
        return nil
    }
    
    func validAttributesForMarkedText() -> [NSAttributedString.Key] {
        return []
    }
    
    func firstRect(forCharacterRange range: NSRange, actualRange: NSRangePointer?) -> NSRect {
        // the emoji picker uses the returned rect to position itself
        var rect = self.bounds
        rect.origin.x = NSMidX(rect)
        rect.size.width = 0
        return self.window!.convertToScreen(self.convert(rect, to:nil))
    }
    
    func characterIndex(for point: NSPoint) -> Int {
        return 0
    }
    
}

NSTextInputClient 需要一个 NSTextInputContextNSViewinputContext 返回上下文如果该类符合 NSTextInputClient 除非实现了 isEditable 并返回 false。标签不返回 NSTextInputContext,解决方案是覆盖 inputContext:

class MyTextField: NSTextField, NSTextInputClient {
    
    var myInputContext : NSTextInputContext?

    override var inputContext: NSTextInputContext? {
        get {
            if myInputContext == nil {
                myInputContext = NSTextInputContext(client:self)
            }
            return myInputContext
        }
    }

    // and the same methods as the button, set self.stringValue instead of self.title in insertText(_:replacementRange:)

 }

关于swift - macOS:从 CharacterPalette 获取表情符号(修订版),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62865658/

相关文章:

c++ - 代码/文本编辑器不需要实际的程序本身吗?

java - Mac 上的 SWT 问题

objective-c - Swift 数组到 AnyObject 错误

ios - 检查 URL 在 Swift 4 中是否有效

Swift:格式化字符串宽度

c++ - Linux - 从 Sudo 读取文件 - "Permission Denied"

objective-c - 访问命名管道以进行读写

python - OpenCV 将帧写入文件 python

swift - 试图清除文档文件夹 Swift

json - firebase 数组解析 json 总是失败