SwiftUI получает NSViewRepresentable для автоматической перерисовки

Я представил стилизованное текстовое поле NSViewRepresentable в своем макете, чтобы использовать поле текстового редактора с атрибутами. Я хочу влиять на содержимое этого текстового поля с помощью некоторых кнопок.

Я могу изменить стилизованные атрибуты поля, но оно никогда не обновляется, если я не нажму его вручную. Должен быть способ сделать это автоматически. Что я пропустил?

      import SwiftUI
import Combine

let publisher = PassthroughSubject<Bool,Never>()

struct ContentView: View
{
    @State var mytext = NSMutableAttributedString(string: "")
    var attributedString: NSMutableAttributedString
    @State var textViewID: Int = 0
    
    init()
    {
        attributedString = NSMutableAttributedString(
            string: "Hello World!",
            attributes: [.font: NSFont.systemFont(ofSize: 18)])
        _mytext = State(initialValue: attributedString)
    }
    
    var body: some View
    {
        VStack
        {
            Button("Bold")
            {
                attributedString.addAttribute(.font, value: NSFont.boldSystemFont(ofSize: 18), range: NSRange(location: 0, length: 5))
                mytext = attributedString
                publisher.send(true)
            }
        }
        TextView(text: $mytext)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .id(textViewID)
            .onReceive(publisher) { ( stateTo ) in
                textViewID += 1
            }

    }
}

struct TextView: NSViewRepresentable
{
    @Binding var text: NSMutableAttributedString
    
    func makeNSView(context: Context) -> NSTextView 
    {
        NSTextView()
    }
    
    func updateNSView(_ nsView: NSTextView, context: Context)
    {
        nsView.textStorage?.setAttributedString(text)
    }

}

Или я что-то упускаю и есть более простой способ?

1 ответ

Подозреваемый обошел это и переключился на более крупную рыбу. Но для тех, кто натыкается на что-то подобное :-)

Проблема с кодом в примере заключается в том, что в нем нет ничего, что SwiftUI мог бы использовать, чтобы определить, когда ему нужно перерисовать.

Конкретно@State var mytext: NSMutableAttributedStringсодержит ссылку на ту же атрибутированную строку, что иattributedStringделает. Следовательно, действие кнопкиmytext = attributedStringэто от pov SwiftUI нооп.

Два подхода к тому, чтобы заставить его работать.

Во-первых, как сейчас, когда одна и та же строка сохраняется и изменяется на месте, а перерисовка действительно запрашивается явно. В этом случае действие «Кнопка» можно очистить, чтобы устранить необходимость в «Объединить» и «onReceiveтаким образом:

      Button("Bold") {
   mytext.addAttribute(.font, value: NSFont.boldSystemFont(ofSize: 18), range: NSRange(location: 0, length: 5))
   textViewID = mytext.hashValue
}

Или, во-вторых, избавьтесь от необходимости использовать атрибуты Combine, onReceive и id, удалив мутацию на месте. Вместо этого замените новой строкой атрибута, которую заметит SwiftUI, например:

      Button("Bold") {
   let newAttributedString:NSMutableAttributedString = mytext.mutableCopy() as! NSMutableAttributedString
   newAttributedString.addAttribute(.font, value: NSFont.boldSystemFont(ofSize: 18), range: NSRange(location: 0, length: 5))
   mytext = newAttributedString
}

Позже, вероятно, немного более идиоматичен SwiftUI.

Другие вопросы по тегам