Search code examples
macosswiftuiswiftui-list

Bad performance of large SwiftUI lists on macOS


I have a SwiftUI app which displays large lists of 1000 to 5000 items. I noticed that on macOS displaying such a long list has very bad performance. It takes several seconds for SwiftUI to render the list. This is independent of the complexity of the row views. Even if the rows are only Text() views.

On iOS, however, the same list would render almost instantaneously.

My code for the list view looks like this:

struct WordList: View {
    
    @EnvironmentObject var store: Store
    @State var selectedWord: RankedWord? = nil

    var body: some View {
        List(selection: $selectedWord) {
            ForEach(store.words) { word in
                HStack {
                    Text("\(word.rank)")
                    Text(word.word)
                }
                .tag(word)
            }
        }
    }
}

Does anybody know some tricks to speed this up? Or is this a general problem on macOS 12 and we need to hope Apple improves this in the next major OS update?

I have also created a very simple sample app to test and demonstrate the list performance and of which the code above is taken from. You can browse / download it on GitHub

Update for Ventura

List performance on Ventura has significantly improved over Monterey. So no additional optimization might be necessary.


Solution

  • If you want to stay with List because you need all this nice features like selection, reordering, easy drag & drop... you will have to help SwiftUI estimate the total height of your list by having a fixed size for your rows. (I think this is the same in UIKit where performance will significantly improve if you are able to estimate the row height for each entry.)

    So in your example, modify your row code as follows:

    HStack {
        Text("\(word.rank)")
        Text(word.word)
    }
    .frame(width: 500, height: 15, alignment: .leading)
    .tag(word)
    

    I know it is an ugly solution because it doesn't dynamically adjust to the font size but it reduces the rendering time on my M1 Max based Mac from 2s down to 0.3s for a list of 10,000 words.