Могу ли я остановить отставание макета NSViewRepresentable для NSTextView?
Я использую внутреннюю часть в приложении SwiftUI.
Размер правильно изменяется до высоты NSTextView, поэтому текст перетекает в несколько строк, но при первом рендеринге после создания он показывает только одну строку .
Похоже, что в этом первом кадре отображается правильно, с текстом, перенесенным в нужной точке, но не отображается полная высота.
Создается, и текст устанавливается наNSTextStorage
вmakeCoordinator()
метод , т.е. доNSViewRepresentable
запрашивает вид.
Я переопределяю, чтобы установитьintrinsicContentSize
:
override func layout() {
invalidateIntrinsicContentSize()
super.layout()
}
override var intrinsicContentSize: NSSize {
layoutManager!.ensureLayout(for: textContainer!)
return CGSize(width: -1.0,
height: ceil(layoutManager!.usedRect(for: textContainer!).size.height))
}
Я думаю, что на эту проблему намекает «помпон», упомянутый в этом посте . Метод отслеживания высоты отличается, но также не может установить первый кадр.
Есть ли что-нибудь, что я могу сделать, чтобы ускорить макет наNSTextView
? Представление не имеет фиксированной ширины, поэтому оно должно перемещаться динамически.
Вот минимальная репродукция:
import SwiftUI
let font = NSFont.systemFont(ofSize: 20, weight: .bold)
let testString = "This long string demonstrates how its full extent is not shown on the first frame of an NSTextView rendered within an NSViewRepresentable."
let fontAttribute = [NSAttributedString.Key.font: font]
let testText = NSAttributedString(string: testString, attributes: fontAttribute)
class InternalTextView: NSTextView
{
init() {
super.init(frame: NSRect.zero)
setContentHuggingPriority(.defaultHigh, for: .vertical)
}
override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) {
super.init(frame: frameRect, textContainer: container) }
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func layout() {
invalidateIntrinsicContentSize()
super.layout()
}
override var intrinsicContentSize: NSSize {
layoutManager!.ensureLayout(for: textContainer!)
return CGSize(width: -1.0,
height: ceil(layoutManager!.usedRect(for: textContainer!).size.height))
}
}
class TextViewCoordinator: NSObject {
private(set) var view: InternalTextView
init(withText text: NSAttributedString) {
view = InternalTextView()
view.textStorage?.setAttributedString(text)
super.init()
}
}
struct TextView: NSViewRepresentable
{
let text: NSAttributedString
func makeNSView(context: Context) -> NSTextView { context.coordinator.view }
func updateNSView(_ nsView: NSTextView, context: Context) { }
func makeCoordinator() -> TextViewCoordinator { TextViewCoordinator(withText: text) }
}
struct ContentView: View
{
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State var showText: Bool = false
var body: some View {
HStack {
Spacer()
ScrollView {
VStack {
if showText {
ForEach(1..<10) { _ in
TextView(text: testText)
} }
Spacer()
}.frame(maxWidth: 600)
}
Spacer()
}
.onReceive(timer) { _ in
showText.toggle()
}
}
}
Кадр 1:
Кадр 2:
1 ответ
У меня была очень похожая проблема с ViewRepresentable на iOS, где я использовал CoreText для выполнения некоторых задач по рендерингу текста. Симптомы были практически идентичны.
Решением в моем случае было переопределить sizeThatFits() в ViewRepresentable и предоставить ему правильную информацию (вычисление высоты текста для фрейма в макете) в исходном представлении.
func sizeThatFits(_ proposal: ProposedViewSize, uiView: <YOUR VIEW>, context: Context) -> CGSize? {
return uiView.sizeThatFits(CGSize(width: proposal.width ?? 0, height: proposal.height ?? 0))
}
Я не уверен, что это сработает в вашей конкретной ситуации, но я потратил около недели на то, чтобы рвать на себе волосы, пытаясь сделать одно и то же, поэтому решил поделиться.