SwiftUI - Make content below a List scrollable as the List scrolling progresses

Viewed 264

I have a list with many elements and two buttons below the list. What I want to achieve is to show the buttons after the user scrolls to the (almost) end of the list. Here is my code:

import SwiftUI

struct ContentView: View {
    var body: some View {
        let numbers = [1, 3, 2, 6, 7, 4, 43, 42, 4, 7, 3, 7, 3, 2, 1, 4, 765, 423, 5345, 5345, 345, 4, 8, 21]
        
        List {
            ForEach(numbers, id: \.self) { number in
                Text(number.description)
            }
        }
        ButtonsStack()
        
    }
}

struct ButtonsStack: View {
    var body: some View {
        VStack {
            Button(action: {}, label: {
                Text("Button")
                    .foregroundColor(.blue)
            })
            Button(action: {}, label: {
                Text("Button2")
                    .foregroundColor(.blue)
            })
        }
    }
}

This results in buttons being fixed at the bottom (showing all the time):

fixedButtons

If I move the ButtonsStack() inside the List, than the buttons will be put as a row of the List which is also not what I want:

buttonsInsideAList

So what I would like to have is buttons as on the first picture, but showing up first when the user scrolls to the bottom of the list. How can I achieve it ? I tried putting everything in a ScrollView but it didn't work.

2 Answers

You have to check if the user has scrolled the list to the end, and condition the appearance of the buttons to that. The easiest way is to check when the last line appears on the screen (onAppear) :

struct ContentView: View {
    @State private var thisIsTheEnd = false
    var body: some View {
        let numbers = [1, 3, 2, 6, 7, 4, 43, 42, 4, 7, 3, 7, 3, 2, 1, 4, 765, 423, 5345, 5345, 345, 4, 8, 21]
        
        VStack {
            List {
                ForEach(numbers.indices, id: \.self) { index in
                    if index == numbers.count - 1 {
                        Text(numbers[index].description)
                            .onAppear {
                                thisIsTheEnd = true
                            }
                    } else {
                        Text(numbers[index].description)
                    }
                }
            }
            if thisIsTheEnd {
                ButtonsStack()
            }
        }
    }
}

It seems like what you're after is setting a footer to the list. In that case, I think this should do it:

struct ContentView: View {
    var body: some View {
        let numbers = [1, 3, 2, 6, 7, 4, 43, 42, 4, 7, 3, 7, 3, 2, 1, 4, 765, 423, 5345, 5345, 345, 4, 8, 21]
    
        List {
            Section(footer: ButtonsStack()) {
                ForEach(numbers, id: \.self) { number in
                    Text(number.description)
                }
            }
        }
    }
}
Related