You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI实现指针悬停字符高亮的方案咨询

SwiftUI实现字符级悬停高亮(适配全语言)

完全可行,核心思路是基于TextKit(AppKit/UIKit的文本布局框架)处理字符位置检测,避免拆分Text组件的方案,完美适配连写语言(如阿拉伯语、印地语)。以下是具体实现方案:

核心原理

利用NSLayoutManager(TextKit核心组件)准确计算鼠标位置对应的字符索引,动态更新NSAttributedString的高亮属性,再通过NSViewRepresentable/UIViewRepresentable封装成SwiftUI可用视图。这种方式依赖原生文本布局引擎,能自动处理复杂语言的连写、字形组合等逻辑。

macOS平台实现代码

import SwiftUI
import AppKit

struct HoverHighlightTextView: NSViewRepresentable {
    var text: String
    @Binding var highlightedRange: NSRange?
    
    func makeNSView(context: Context) -> NSTextView {
        let textView = NSTextView()
        textView.string = text
        textView.isEditable = false
        textView.isSelectable = false
        textView.backgroundColor = .clear
        
        // 添加鼠标追踪区域,监听移动事件
        let trackingArea = NSTrackingArea(
            rect: textView.bounds,
            options: [.mouseMoved, .activeAlways, .inVisibleRect],
            owner: context.coordinator,
            userInfo: nil
        )
        textView.addTrackingArea(trackingArea)
        return textView
    }
    
    func updateNSView(_ nsView: NSTextView, context: Context) {
        let attrString = NSMutableAttributedString(string: text)
        // 设置默认文本颜色
        attrString.addAttribute(
            .foregroundColor,
            value: NSColor.black,
            range: NSRange(location: 0, length: text.count)
        )
        // 应用高亮样式到目标字符
        if let range = highlightedRange {
            attrString.addAttribute(
                .foregroundColor,
                value: NSColor.systemBlue,
                range: range
            )
        }
        nsView.textStorage?.setAttributedString(attrString)
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    
    class Coordinator: NSObject {
        let parent: HoverHighlightTextView
        
        init(parent: HoverHighlightTextView) {
            self.parent = parent
        }
        
        override func mouseMoved(with event: NSEvent) {
            guard let textView = event.window?.contentView?.hitTest(event.locationInWindow) as? NSTextView else {
                parent.highlightedRange = nil
                return
            }
            // 转换鼠标坐标到文本视图坐标系
            let localLocation = textView.convert(event.locationInWindow, from: nil)
            // 通过布局管理器获取鼠标对应的字符索引
            let charIndex = textView.layoutManager.characterIndex(
                for: localLocation,
                in: textView.textContainer,
                fractionOfDistanceBetweenInsertionPoints: nil
            )
            // 校验索引有效性,更新高亮范围
            guard charIndex < textView.string.count else {
                parent.highlightedRange = nil
                return
            }
            parent.highlightedRange = NSRange(location: charIndex, length: 1)
        }
    }
}

// 视图使用示例
struct ContentView: View {
    @State private var highlightedRange: NSRange?
    
    var body: some View {
        HoverHighlightTextView(
            text: "مرحبا بالعالم 🌍 Hello World こんにちは",
            highlightedRange: $highlightedRange
        )
        .frame(width: 400, height: 100)
        .padding()
    }
}

关键说明

  1. 适配复杂语言:NSLayoutManager会自动处理连写字符、字形组合等布局逻辑,不会出现拆分Text导致的字符破碎问题。
  2. 跨平台调整:如果是iOS/iPadOS平台,只需将NSViewRepresentable换成UIViewRepresentable,用UITextView替代NSTextView,并通过触摸事件(或iPad鼠标支持)实现位置检测,核心逻辑完全一致。
  3. 样式扩展:除了颜色高亮,还可以修改字体、添加下划线等其他属性,只需在NSMutableAttributedString中添加对应的属性即可。

内容的提问来源于stack exchange,提问作者Yousif Ismat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:20:34