Carl
Carl

Reputation: 618

Swift / iOS 16 Empty SwiftUI List Background Color

My app is built in SwiftUI and mostly works as is with iOS 16 apart from a couple of design quirks which I'm currently working on a fix for.

One of the quirks is the background colours of lists. Previously I have used Introspect to set the color of the background on the lists but as Lists have been reimplemented in iOS16 this no longer works.

I have solved this for iOS 16 devices by using the new scrollContentBackground modifier:

List() {
   some foreach logic here
}
.background(color)
.scrollContentBackground(.hidden)

This works as expected apart from one issue.

When the list is empty the background color is ignored, It shows a white or black background (Not even the grouped background colours) depending on the light or dark mode setting.

Has anybody else come across this issue (or am I doing something wrong?) and if so what solutions have you come up with?

Thanks, C

Upvotes: 10

Views: 3567

Answers (4)

Abdurakhmon
Abdurakhmon

Reputation: 3080

here's what I have done

if data.count > 0 {
    List()
} else {
    Color.clear
}

Upvotes: 9

Ashish Dutt
Ashish Dutt

Reputation: 1

I was looking for the same and I got an idea from @Rhys Morgan answer. When the list is empty, I added a TextView with empty string and used the .listRowBackground(Color.clear) with and it worked.

List {
                    ForEach(items) { item in
                        VStack(alignment: .leading) {
                            Text(item.task ?? "")
                                .font(.headline)
                                .fontWeight(.bold)
                            
                            Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                                .font(.footnote)
                                .foregroundColor(.gray)
                            
                        }
                    }
                    .onDelete(perform: deleteItems)
                    if(items.isEmpty){
                        Text("")
                            .listRowBackground(Color.clear)
                    }
                }//: LIST
                .listStyle(InsetGroupedListStyle())
                .shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.3), radius: 12)
                .padding(.vertical,0)
                .frame(maxWidth: 640)
                .background(.clear)
                .scrollContentBackground(.hidden)

Watch the result here

Upvotes: 0

Carl
Carl

Reputation: 618

May not work for everyone but I have a solute for my own problem.

I am using an overlay to present a message when the list is empty so I decided to do the old ZStack trick in here and it seems to work as expected.

Example:

List() {
    ForEach(data, id: \.id) { item in
      // some foreach logic here
    }
}
.background(Color.red)
.scrollContentBackground(.hidden)
.overlay(Group {
    if(data.isEmpty) {
        ZStack() {
            Color.red.ignoresSafeArea()
            Text("Empty List!")
        }
    }
})

Hope this helps somebody else!

Upvotes: 5

Rhys Morgan
Rhys Morgan

Reputation: 123

The unfortunate solution is to add an element to your List to ensure that it isn't empty.

I found your question while looking it up for myself, and I found that the only way to remove the white/black background from the List was to add any non-EmptyView view. However, that doesn't mean it has to be visible, appear on the screen, or display any content.

Try the following:

List {
  Group {
    ForEach(data, id: \.id) { item in
      // do your ForEach logic here
    }

    if data.isEmpty {
      Spacer()
    }
  }
  .listRowBackground(Color.clear)
}
.scrollContentBackground(.hidden)

If Spacer() doesn't work for you for some reason, try:

Text("ListFix")
  .hidden()
  .accessibility(hidden: true)

instead, or something along those lines. .accessibility(hidden: true) is important to add, otherwise it'll read that text to anyone using VoiceOver.

Consider this an opportunity to add some useful empty state message to your List, perhaps. That's what I ultimately did, moving the empty state inside my List, rather than wrapping the entire List in an if !data.isEmpty condition. But if you don't want an empty state, just adding a single view that you can hide will seem to do the job.

Upvotes: 4

Related Questions