skip to Main Content

I’m trying to achieve a specific UI design in SwiftUI where the bottom section of my List has a different background color than the top section. For example, the "Your Medications" Section has a different background than the top "Log" Section:

Goal layout:

enter image description here

Here some example code. I wonder if I am supposed to use two Lists instead. If I use two Lists though and nest it in a ScrollView, the height of the lists needs to be specified. I am working with dynamic content, though so I don’t think that is ideal.

class ProtocolMedication {} // Example model

struct HomeView: View {
    @Query private var protocolMedications: [ProtocolMedication]
    
    var body: some View {
        NavigationStack {
            List {
                // Upper sections with default background
                Section {
                    Text("Content 1")
                } header: {
                    Text("Log")
                }
                // Bottom section that needs different background
                Section {
                    ForEach(protocolMedications) { medication in
                        Text(medication.name)
                    }
                } header: {
                    Text("Your Medications")
                }
            }
            .listStyle(.insetGrouped)
        }
    }
}

2

Answers


  1. You could use .listRowBackground():

     } header: {
        Text("Your Medications")
     }
     .listRowBackground(Color.purple.opacity(0.15))
     ...
    
    Login or Signup to reply.
  2. If I understand correctly, you want a different color for the outer background around the section, this being the list group background.

    • The default background can be hidden by applying .scrollContentBackground(.hidden) to the List. Then you can apply your own .background to the List.

    • The default background is UIColor.systemGroupedBackground, so this can be used as the first layer in a ZStack that you show in the background.

    • Show the color for the section background as the next layer in the ZStack.

    • The position of the second color can be matched to the section header using .matchedGeometryEffect with alignment: .top.

    • Apply .frame(maxWidth: .infinity, alignment: .leading) to the content of the section header, so that .top (used for matching position) is then the middle of the row, not just the middle of the content.

    • Padding can be used to tweak the position, if necessary.

    If you had more sections then you can use the same technique for them too. Each section would have its own layer in the ZStack. In this case, you might want to avoid colors that are partially transparent (colors with opacity), unless you want the transparency effects to combine.

    For the list rows themselves, set your own background using .listRowBackground.

    struct HomeView: View {
        @Namespace private var ns
    
        var body: some View {
            NavigationStack {
                List {
                    // Upper sections with default background
                    Section {
                        Text("Content 1")
                    } header: {
                        Text("Log")
                    }
                    // Bottom section that needs different background
                    Section {
                        ForEach(1..<6) { i in
                            Text("Row (i)")
                        }
                        .padding(.leading, 80)
                        .frame(minHeight: 80)
                        .listRowBackground(
                            HStack(spacing: 0) {
                                Image(systemName: "pills")
                                    .resizable()
                                    .scaledToFit()
                                    .frame(width: 50)
                                    .symbolRenderingMode(.hierarchical)
                                    .padding(.horizontal)
                                    .frame(maxHeight: .infinity)
                                    .background(Color(.tertiarySystemBackground).gradient)
                                Color(.secondarySystemGroupedBackground)
                            }
                        )
                    } header: {
                        Text("Your Medications")
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .matchedGeometryEffect(
                                id: "Section2",
                                in: ns,
                                anchor: .top
                            )
                    }
                }
                .listStyle(.insetGrouped)
                .scrollContentBackground(.hidden)
                .background {
                    ZStack {
                        Color(.systemGroupedBackground)
                        Color.yellow
                            .opacity(0.5)
                            .padding(.top, -16)
                            .matchedGeometryEffect(
                                id: "Section2",
                                in: ns,
                                properties: .position,
                                anchor: .top,
                                isSource: false
                            )
                    }
                    .ignoresSafeArea()
                }
            }
        }
    }
    

    Screenshot

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search