Swift / SwiftUI

Rastreando fontes de valor para prevenir atualizações recursivas em SwiftUI

Ao envolver um UITextView ou NSTextView em SwiftUI, atualizações recorrentes podem ocorrer devido ao ciclo de escrita e leitura do binding. Para evitar comparações de string custosas, o artigo sugere usar transações personalizadas para marcar a origem das atualizações, permitindo que wrappers evitem operações desnecessárias.

Compartilhar
Tracking value sources to prevent recursive SwiftUI updates social preview

Rastreando fontes de valor para prevenir atualizações recursivas em SwiftUI

Quando envolvemos um UITextView ou NSTextView para uso em SwiftUI, nosso delegado de texto recebe cada atualização conforme o usuário digita, escrevendo isso de volta a um binding do SwiftUI. Ao fazer isso, no entanto, é acionada uma atualização do SwiftUI com o novo valor. Aplicar o valor sem condições adiciona trabalho desnecessário na formatação de texto e pode mover o ponto de inserção para o início enquanto o usuário está digitando. Para evitar isso, envoltórios comumente compararam as strings antes de atualizar a visualização de texto, mas a comparação de string pode ser muito cara.

Neste post, exploraremos como valores personalizados de transação podem rotular atualizações que originam em nossas visualizações de texto envolvidas, permitindo-nos evitar comparações caras de strings em updateUIView(_:context:) e updateNSView(_:context:).

Analisando um UIViewRepresentable típico, veremos algo como isso:


func textViewDidChange(_ textView: UITextView) {
    text = textView.text
}


@Binding var text: String

func updateUIView(_ textView: UITextView, context: Context) {
    
    guard textView.text != text else { return }
    textView.text = text
}

A comparação de uma string potencialmente grande pode se tornar muito cara, mas a comparação é necessária. Sempre que o editor escreve um novo valor no binding em textViewDidChange(_:), a propriedade @State ou classe @Observable que recebe a alteração invalida seus consumidores. Nosso UIViewRepresentable ou NSViewRepresentable é um desses consumidores através de seu binding, levando à chamada do respectivo método de atualização com o valor que acabamos de escrever.

As transações do SwiftUI nos permitem registrar onde uma atualização de propriedade originou. Envolvermos uma escrita em um binding ou propriedade observável dentro de withTransaction(::_:) e anexamos um valor personalizado, como um ObjectIdentifier. Quando o SwiftUI envia a atualização de volta, o wrapper lê o valor personalizado da transação.

func textViewDidChange(_ textView: UITextView) {
    withTransaction(
        \.originatingTextView,
        ObjectIdentifier(textView)
    ) {
        text = textView.text
    }
}
func updateUIView(_ textView: UITextView, context: Context) {
    
guard context.transaction.originatingTextView != ObjectIdentifier(textView) else {
        return
    }
    textView.text = text
}

No momento de definir uma transação personalizada, assim como um valor de ambiente personalizado, podemos usar o macro @Entry do SwiftUI para declarar o valor e seu padrão diretamente em uma extensão sobre Transaction.

extension Transaction {
    @Entry var originatingTextView: ObjectIdentifier? = nil
}

Este método não está limitado a visualizações de texto envoltas. É útil também para qualquer visualização envolta UIView ou NSView com um gesto que alimente até o SwiftUI. Marcar as escritas impulsionadas por gestos evita que os mesmos valores retornem na próxima iteração do ciclo de execução e sobrescrevam atualizações posteriores de gestos.

Fonte original

Conteúdo traduzido e adaptado pela redação do Notícias Mobile. Confira também a matéria na fonte original.

Leia a matéria completa