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

选中内容变化时通过NSViewRepresentable传递NSTextView属性至SwiftUI父视图

How to Sync NSTextView Selection Attributes with SwiftUI Parent View

Got it, let's fix this problem for you. The main hurdle here was not realizing you can grab the NSTextView instance directly from the selection change notification — no custom delegates or extra subscriptions needed. Here's how to make it work:

Updated Coordinator Implementation

class Coordinator: NSObject, NSTextViewDelegate {
    var parent: TextViewMacOS
    
    init(_ parent: TextViewMacOS) {
        self.parent = parent
    }
    
    func textViewDidChangeSelection(_ notification: Notification) {
        // Pull the active NSTextView from the notification's object
        guard let textView = notification.object as? NSTextView else { return }
        
        // Sync typing attributes directly to the SwiftUI binding
        parent.currentTypingAttributes = textView.typingAttributes
        
        // Get attributes at the current selection/cursor position
        let selectedRange = textView.selectedRange()
        if let attributes = textView.textStorage?.attributes(at: selectedRange.lowerBound, effectiveRange: nil) {
            parent.currentTextAttributes = attributes
        }
    }
}

Key Details to Note

  • Access the TextView from the Notification: The notification.object property is exactly the NSTextView that triggered the selection change. A safe downcast gives you full access to all its properties and methods.
  • Handle Both Selection & Cursor States: Whether the user has text selected or just a cursor, using selectedRange.lowerBound ensures you fetch attributes at the active position.
  • Mixed Attributes Edge Case: If the selected text has mixed formatting (e.g., part bold, part italic), this code will grab attributes from the start of the selection. If you need to detect mixed attributes, you can compare the returned effectiveRange to the full selected range.

Full Updated TextViewMacOS Struct

Here's the complete implementation with basic NSTextView setup included:

import SwiftUI

struct TextViewMacOS: NSViewRepresentable {
    @Binding var currentTypingAttributes: [NSAttributedString.Key : Any]
    @Binding var currentTextAttributes: [NSAttributedString.Key : Any]
    
    class Coordinator: NSObject, NSTextViewDelegate {
        var parent: TextViewMacOS
        
        init(_ parent: TextViewMacOS) {
            self.parent = parent
        }
        
        func textViewDidChangeSelection(_ notification: Notification) {
            guard let textView = notification.object as? NSTextView else { return }
            
            parent.currentTypingAttributes = textView.typingAttributes
            
            let selectedRange = textView.selectedRange()
            if let attributes = textView.textStorage?.attributes(at: selectedRange.lowerBound, effectiveRange: nil) {
                parent.currentTextAttributes = attributes
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    public func makeNSView(context: Context) -> NSTextView {
        let textView = NSTextView()
        textView.delegate = context.coordinator
        textView.typingAttributes = currentTypingAttributes // Set initial attributes
        return textView
    }
    
    public func updateNSView(_ textView: NSTextView, context: Context) {
        // Sync SwiftUI attributes to NSTextView (avoid loops by checking for changes)
        if textView.typingAttributes != currentTypingAttributes {
            textView.typingAttributes = currentTypingAttributes
        }
    }
}

Why This Approach Works

You don't need to reinvent the wheel here. AppKit's standard NSTextViewDelegate pattern provides everything you need via the notification object. This keeps your code clean, aligned with platform best practices, and avoids unnecessary complexity like custom delegates.

内容的提问来源于stack exchange,提问作者santi.gs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:01:25