Home > database >  Keystroke lag when typing in Textfield in Table or List
Keystroke lag when typing in Textfield in Table or List

Time:09-27

I'm creating a document-based application where data is represented by TextFields in a TableView (it could also be a List, the same issue occurs). When the app SwiftUI app on an Intel MacBook Air, I get a lot of keyboard lag whenever there are more than a dozen rows in my table. It's present on the Apple Studio too, but less noticeable. I've tried changing the table into a List and LazyVStack, but it doesn't seem to make much difference. Using the Swift UI instrument, it looks to me like every TextField on the page is being redrawn on every keystroke, even though their values haven't changed.

I also tried using a custom TextField with a debounce added in (with Screenshot of the application in use (not just the working example or cut-down code).

CodePudding user response:

As mentioned in the comments on original post.

TL;DR; For those encountering a similar lag issue, the solution here was to replace the declaration of Vocab as a struct with the use of an ObservableObject class, i.e. Vocab's definition becomes class Vocab: ObservableObject, Identifiable, Codable, Equatable.

Might also want to have a look at https://www.hackingwithswift.com/books/ios-swiftui/adding-codable-conformance-for-published-properties if @Published properties in the class have to be Codable

In a bit more detail

When struct Vocab is used each keystroke (because it is a value type) creates a new (original data change) instance of the Vocab struct.

The problem [0] with this struct new instance is that SwiftUI cannot detect the singular property change and trigger just updating its corresponding TextField [1].

Instead SwiftUI handles each new keystroke driven struct instance as if it is a completely unrelated Vocab object; for which it has to update every TextField in the Table's entry row.

It's the updating of all of the TextFields in the entry row that causes the perceived lag.

By contrast the solution - using an ObservableObject class - enables binding the TextFields to a property on an object where the instance does not change on each keystroke. Consequently SwiftUI is able to detect and update just the individual entry changes.

The final piece in the puzzle is that when using an ObservableObject. The @Published properties that update Views nicely take some extra effort to enable them to conform with the Codeable protocol. For how to add that conformance there is a nice explanation over [here[( https://www.hackingwithswift.com/books/ios-swiftui/adding-codable-conformance-for-published-properties)

Other bits
  1. If running on higher spec machines - or with fewer properties - issues like these can be difficult to spot.
  2. Other approaches might be possible. For instance, if it's practicable within the context of the rest of the app, relaxing Vocab's Equatable compliance [1] might be enough to enable SwiftUI to do something more clever when determining what TextFields need recomputing.

[0] In this context; generally though, preferring value types such as structs is seen as good practice because it reduces the risk of unexpected side-effects.

[1] Possibly this might also be addressable by relaxing the implementation of Equatable conformance on the struct to just being based on id equivalence.

  • Related