I'm using a ForEach
to loop over an array of structures and display a View
for each item of the array.
When I'm mutating a single item, the main view re-render every element of the array. Is there a way to avoid this when using structures? I'd like that only the view corresponding to my modified item to be re-rendered.
Is it achievable with structures or do I need to use classes with ObservedObjects
?
A simplified version would look like this (the real version uses the Defauts package with an array and the item view is way more expensive, but the idea is the same).
import SwiftUI
import PlaygroundSupport
struct Player {
var name: String
var score: Int
}
struct PlayerView: View {
@Binding var player: Player
var body: some View {
let _ = Self._printChanges()
return HStack(spacing: 12) {
Text(player.name).font(.system(size: 20))
Text("\(player.score)").font(.system(size: 20))
Button("plus") { player.score += 1 }.font(.system(size: 20))
}
}
}
struct PlayersView: View {
@State var players = [
Player(name: "John", score: 12),
Player(name: "Jane", score: 15)
]
var body: some View {
VStack {
ForEach($players, id: \.name) { player in
PlayerView(player: player)
}
Button("inc", action: {players[0].score += 10})
}
}
}
PlaygroundPage.current.setLiveView(PlayersView())
Just make model Equatable
so rendering engine could detect if dependent property is really changed.
Tested with Xcode 13.4 / iOS 15.5
struct Player: Equatable { // << here !!
var name: String
var score: Int
}