5

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

Carl
  • 548
  • 1
  • 4
  • 21

4 Answers4

4

here's what I have done

if data.count > 0 {
    List()
} else {
    Color.clear
}
Abdurakhmon
  • 2,813
  • 5
  • 20
  • 41
3

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!

Carl
  • 548
  • 1
  • 4
  • 21
3

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.

Rhys Morgan
  • 83
  • 3
  • 8
  • 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. – Carl Dec 02 '22 at 10:03
  • 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. – Rhys Morgan Dec 02 '22 at 12:35
  • 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. – Carl Dec 02 '22 at 13:55
  • 1
    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. – Rhys Morgan Dec 02 '22 at 14:52
  • You could just show a "No Data" element that has something like `foregroundStyle(.secondary)` to make it stand out from the regular list items. – Lerk Aug 08 '23 at 00:42
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