10

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

1
  • I just discovered that this absurd defect only afflicts iOS 16, not 17. They finally added a background-color control to lists, and it was broken. Commented Mar 4, 2024 at 2:08

4 Answers 4

11

here's what I have done

if data.count > 0 {
    List()
} else {
    Color.clear
}
Sign up to request clarification or add additional context in comments.

1 Comment

Best answer here, did the trick for me 👍
5

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!

2 Comments

Sry bro, this still doesn't solve the problem I'm having
@DoubleShy0N what problem are you having...
4

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.

5 Comments

My answer below shows how to color the background and add a state message if the list is empty using the "overlay" modifier. This is the cleanest & simplest way of overcoming this issue at the moment.
It doesn't always solve the problem though, because if the List is empty, iOS 16 ignores the .scrollContentBackground(.hidden) and gives the empty list a white/black background. Ensuring that there is some content in the list, even if it's hidden, makes it work again.
That's what the overlay is for, It contains a ZStack with a full screen color and the empty message presented in front of it.
That only helps if you've got a solid colour background behind the List that can be easily replicated. If you've got a gradient behind it, it's really difficult (next to impossible across devices with different screen sizes) to get it to show through. Adding literally anything to the List guarantees that it'll honour the .scrollContentBackground(.hidden), and remove the white/black background, and allow your actual background to show through.
You could just show a "No Data" element that has something like foregroundStyle(.secondary) to make it stand out from the regular list items.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.