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
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 TextField
s in the entry row that causes the perceived lag.
By contrast the solution - using an ObservableObject
class - enables binding the TextField
s 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
- If running on higher spec machines - or with fewer properties - issues like these can be difficult to spot.
- Other approaches might be possible. For instance, if it's practicable within the context of the rest of the app, relaxing
Vocab
'sEquatable
compliance [1] might be enough to enableSwiftUI
to do something more clever when determining whatTextField
s 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.