NSTextView 自动适应内容大小

问题描述 投票:0回答:1

主要问题:

SwiftUI 的

Text()
无法在内部填充大量文本。如果文本有太多符号或字体太大,则会显示以下错误:

[Window] 警告:Window SwiftUI.AppKitWindow 0x13be8e970 在非活动应用程序的前面排序,并且可能在活动应用程序的窗口下方排序。 2023-08-19 22:48:49.743611 + 0300 Videq [83718:3987311] - [<_TtCOCV7SwiftUI11DisplayList11ViewUpdater8PlatformP33_65A81BD07F0108B0485D2E15DE104A7514CGDrawingLayer: 0x6000031d8e40>显示]:忽略虚假图层大小(479.000000,323046.000000),contentsScale 2.000000,后备存储大小(958.000000, 646092.000000) 2023-08-19 22:48:49.791437+0300 Videq[83718:3987311] Metal API 验证已启用

所以我需要使用

Text()
 编写自定义 
NSTextView

我的代码是:

import SwiftUI
import Cocoa

@available(OSX 11.0, *)
public struct AttributedText2: View {
    @Binding var text: AttributedString
    
    var nsText: Binding<NSAttributedString> {
        Binding(get: { NSAttributedString(text) }, set: { _ in })
    }
    
    @State var width: CGFloat = 0
    
    public var body: some View {
        AttributedTextInternal(attributedString: nsText)
    }
}

//@available(OSX 11.0, *)
//public struct AttributedText: View {
//    @Binding var text: NSAttributedString
//
//    public init(attributedString: Binding<NSAttributedString>) {
//        _text = attributedString
//    }
//
//    public var body: some View {
//        AttributedTextInternal(attributedString: $text)
//    }
//}

@available(OSX 11.0, *)
public struct AttributedTextInternal: NSViewRepresentable {
    @Binding var text: NSAttributedString
    
    public init(attributedString: Binding<NSAttributedString>) {
        _text = attributedString
    }
    
    public func makeNSView(context: Context) -> CustTextFld {
        let textView = CustTextFld()
        
        textView.setContent(text: text, makeNotEditable: true)
        
        textView.backgroundColor = .clear
        
        textView.layerContentsPlacement = .top
        textView.layerContentsRedrawPolicy = .crossfade
        
        return textView
    }
    
    public func updateNSView(_ textView: CustTextFld, context: Context) {
        textView.setContent(text: text, makeNotEditable: true)
    }
}

public class CustTextFld: NSTextView {
    func setContent(text: NSAttributedString, makeNotEditable: Bool) {
        self.isEditable = true
        
        self.selectAll(nil)
        self.insertText(text, replacementRange: self.selectedRange())
        
        self.isEditable = !makeNotEditable
    }
    
    // remove cursor
    public override func mouseMoved(with event: NSEvent) {
    }
    
    public override func viewWillDraw() {
        isHorizontallyResizable = true
        isVerticallyResizable = true
        isRichText = true
        isSelectable = false
        
//        isRichText = false
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        let path = NSBezierPath(rect: bounds)
        
        NSColor.white.setStroke()
        
        path.stroke()
    }
}

extension NSAttributedString {
    func height(containerWidth: CGFloat) -> CGFloat {
        let rect = self.boundingRect(with: CGSize.init(width: containerWidth, height: CGFloat.greatestFiniteMagnitude),
                                     options: [.usesLineFragmentOrigin, .usesFontLeading],
                                     context: nil)
        return ceil(rect.size.height)
    }

    func width(containerHeight: CGFloat) -> CGFloat {

        let rect = self.boundingRect(with: CGSize.init(width: CGFloat.greatestFiniteMagnitude, height: containerHeight),
                                     options: [.usesLineFragmentOrigin, .usesFontLeading],
                                     context: nil)
        return ceil(rect.size.width)
    }
}

这里的问题是我能够强制

AttributedText2
的大小,以确保它的宽度和高度正确:

  • 宽度必须与父视图内容相同
  • 高度必须使用
    NSAttributedString.height(containerWidth: )
  • 根据宽度计算

我曾尝试获取内容大小但没有任何结果。我也尝试过做

textView.sizeToFit()
- 也没有结果。尝试使用几何阅读器来获取视图宽度 - 但它破坏了视图,并且视图的大小仍然不正确。

有人有什么想法吗?

swift macos swiftui nsattributedstring nstextview
1个回答
0
投票

如果我理解正确,那么我认为这可以解决问题。您所要做的就是将

AttributedText2
结构替换为以下内容:

public struct AttributedText2: View {
    @Binding var text: AttributedString
    
    var nsText: Binding<NSAttributedString> {
        Binding(get: { NSAttributedString(text) }, set: { _ in })
    }
    
    public var body: some View {
        GeometryReader { geometry in
            AttributedTextInternal(attributedString: nsText)
                .frame(width: geometry.size.width, height: nsText.wrappedValue.height(containerWidth: geometry.size.width))
        }
    }
}

请注意,它使用

GeometryReader
作为尺寸数据。集成后,它就变成了:

import Foundation
import SwiftUI
import Cocoa

@available(OSX 11.0, *)
public struct AttributedText2: View {
    @Binding var text: AttributedString
    
    var nsText: Binding<NSAttributedString> {
        Binding(get: { NSAttributedString(text) }, set: { _ in })
    }
    
    public var body: some View {
        GeometryReader { geometry in
            AttributedTextInternal(attributedString: nsText)
                .frame(width: geometry.size.width, height: nsText.wrappedValue.height(containerWidth: geometry.size.width))
        }
    }
}


@available(OSX 11.0, *)
public struct AttributedTextInternal: NSViewRepresentable {
    @Binding var text: NSAttributedString
    
    public init(attributedString: Binding<NSAttributedString>) {
        _text = attributedString
    }
    
    public func makeNSView(context: Context) -> CustTextFld {
        let textView = CustTextFld()
        
        textView.setContent(text: text, makeNotEditable: true)
        
        textView.backgroundColor = .clear
        
        textView.layerContentsPlacement = .top
        textView.layerContentsRedrawPolicy = .crossfade
        
        return textView
    }
    
    public func updateNSView(_ textView: CustTextFld, context: Context) {
        textView.setContent(text: text, makeNotEditable: true)
    }
}

public class CustTextFld: NSTextView {
    func setContent(text: NSAttributedString, makeNotEditable: Bool) {
        self.isEditable = true
        
        self.selectAll(nil)
        self.insertText(text, replacementRange: self.selectedRange())
        
        self.isEditable = !makeNotEditable
    }
    
    // remove cursor
    public override func mouseMoved(with event: NSEvent) {
    }
    
    public override func viewWillDraw() {
        isHorizontallyResizable = true
        isVerticallyResizable = true
        isRichText = true
        isSelectable = false
        
//        isRichText = false
    }
    
    public override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        
        let path = NSBezierPath(rect: bounds)
        
        NSColor.white.setStroke()
        
        path.stroke()
    }
}

extension NSAttributedString {
    func height(containerWidth: CGFloat) -> CGFloat {
        let rect = self.boundingRect(with: CGSize.init(width: containerWidth, height: CGFloat.greatestFiniteMagnitude),
                                     options: [.usesLineFragmentOrigin, .usesFontLeading],
                                     context: nil)
        return ceil(rect.size.height)
    }

    func width(containerHeight: CGFloat) -> CGFloat {

        let rect = self.boundingRect(with: CGSize.init(width: CGFloat.greatestFiniteMagnitude, height: containerHeight),
                                     options: [.usesLineFragmentOrigin, .usesFontLeading],
                                     context: nil)
        return ceil(rect.size.width)
    }
}

看起来像什么:

© www.soinside.com 2019 - 2024. All rights reserved.