SwiftUI dynamic list of Button

457 Views Asked by At

I created a view showing a list of Button. These buttons are displaying a date. This view is instantiated in my HomeView.

My need is that whenever the user taps one of the button, the date displayed by the tapped button is stored to a variable. Only one button should be selected at a time.

My list of buttons looks like this.

Day.swift

struct Day: View
{ var date: Date @State private var isSelected = false func updateIsSelected()
    { if (isSelected == true)
        {
            isSelected = false
        }
        else
        {
            isSelected = true
        }
    } var body: some View { VStack { Button("\(date.formatted(Date.FormatStyle().day(.twoDigits).month(.twoDigits)))", action: { updateIsSelected() }) }.buttonStyle(.bordered) }
}

DaysPanel.swift

struct DaysPanel: View
{
    var currentDate: Date = User.shared.startDate
    var dayList: [Day] = []

    init(_ nbWeeks: Int)
    {
        for _ in 1 ... nbWeeks
        { addOneWeek() }
    }

    mutating func addOneDay()
    { // Populate the list of Day
        let tmpDay = Day(date: currentDate) dayList.append(tmpDay)

        // Increase the date by a day
        currentDate = currentDate.addingTimeInterval(Double(SECONDS_IN_DAY))
    } mutating func addOneWeek()
    { for _ in 1 ... DAYS_IN_WEEK { addOneDay() }
    }

    var body: some View
    {
        VStack
        {
            HStack
            { Spacer() ScrollView(.horizontal) { HStack { ForEach(0 ..< dayList.count) { index in dayList[index] } } } }
        }
    }
}

I though I could implement a getter method for my Day to retrieve it's status. Then, from the DayPanel, I could have iterated over the dayList and check which one is selected and retrieve it's date. For some reason, my HomeView is not letting me do so, nothing happens when I tap one of the day button.

HomeView.swift

var body: some View
{
    VStack
    {
        self.dayPanel.onTapGesture
        { 
           // Method here to iterate over panel list and retrieve selected button and its value
        }
    }
}
1

There are 1 best solutions below

0
On

A possible solution is to declare a selectedIndex @State variable in DaysPanel and instead of the button in Day use just a styled Text and add a tapGesture in the ScrollView which modifies the selection.

struct Day: View {
    let date: Date

    var body: some View {
        VStack {
            Text(date.formatted(Date.FormatStyle().day(.twoDigits).month(.twoDigits)))
                .padding(8)
                .foregroundColor(.blue)
                .background(Color.gray.opacity(0.25).gradient)
                .clipShape(RoundedRectangle(cornerRadius: 8))
        }
    }
}

struct DaysPanel: View {
    @State var selectedIndex : Int?
    
    var dayList: [Day] = [Day(date: .now), Day(date: .now.addingTimeInterval(86400))]
    
    var body: some View {
        VStack  {
            HStack {
                Spacer()
                ScrollView(.horizontal) {
                    HStack {
                        ForEach(0 ..< dayList.count, id: \.self) { index in
                            dayList[index]
                                .onTapGesture {
                                    selectedIndex = index
                                     print(dayList[index].date)
                                }
                        }
                    }
                }
            }
        }
    }
}

Alternatively drop the Day struct and define the button in DaysPanel

struct DaysPanel: View {
    @State var selectedIndex : Int?
    
    var dayList: [Date] = [.now, .now.addingTimeInterval(86400)]
    
    var body: some View {
        VStack  {
            HStack {
                Spacer()
                ScrollView(.horizontal) {
                    HStack {
                        ForEach(0 ..< dayList.count, id: \.self) { index in
                            Button(dayList[index].formatted(Date.FormatStyle().day(.twoDigits).month(.twoDigits))) {
                                selectedIndex = index
                                print(dayList[index])
                            }
                            .buttonStyle(.bordered)
                        }
                    }
                }
            }
        }
    }
}

You can even keep the date rather than the index

@State var selectedDate: Date?

and in the button action

selectedDate = dayList[index]