SwiftUI:如何根据 Picker 的值更新 ForEach 循环的范围

Posted

技术标签:

【中文标题】SwiftUI:如何根据 Picker 的值更新 ForEach 循环的范围【英文标题】:SwiftUI: how to update the range of a ForEach loop based on the value of a Picker 【发布时间】:2020-07-09 17:55:40 【问题描述】:

所以我试图让 ForEach 循环根据选择的月份(选择器中的值)更新视图循环的次数。就我而言,它们将根据给定年份所选月份的月份中的天数循环。我已经有一个函数可以为我提供每个月的天数,但是,当我将其插入 ForEach 循环时,它只会根据所选的第一个月运行,并在其余时间保持迭代该月的天数.这是我的 ForEach 循环代码:

ForEach(0..<getRange(year: yearIndex, month: monthIndex + indexCheck))  i in
     NavigationLink(destination: ContentView(day: yearData[yearIndex].months[monthIndex].dayInfo[i])) 
          DayRow(day: yearData[yearIndex].months[monthIndex].dayInfo[i])
     

这里是 getRange() 函数:

func getRange(year: Int, month: Int) -> Int 
        return Calendar.current.range(of: .day, in: .month, for: Calendar.current.date(from: DateComponents(year: year + 2020, month: month + 1))!)!.count

yearIndex 变量与三年(2020、2021、2022)的选择器值相关联。这是它的代码:

Picker("Years", selection: $yearIndex) 
     ForEach(0 ..< year.count)  i in
          Text(String(self.year[i])).tag(i)
     

.pickerStyle(SegmentedPickerStyle())

monthIndex 变量与一年中的月份(1 月至 12 月)链接到选择器。这是它的代码:

Picker("Month", selection: $monthIndex) 
     ForEach(0 ..< monthArray.count)  i in
          Text(self.monthArray[i]).tag(i)
     

.padding(.bottom, 2)

我不知道我做错了什么,我不知道该怎么做,所以任何帮助将不胜感激!我对 Swift/SwiftUI 还是很陌生,所以任何关于更好代码的建议也将不胜感激!

编辑:根据要求,这是一个最小的可重现示例:

struct ContentView: View 
    
    @State var year = [2020, 2021, 2022]
    //monthSymbols gets an array of all the months
    @State var monthArray = DateFormatter().monthSymbols!
    @State var yearIndex = 0
    @State var monthIndex = 0
    @State var indexCheck = 0
    @State var indexTest = 0
    
    var body: some View 
        NavigationView 
            List 
                Section 
                    VStack 
                        Picker("Years", selection: $yearIndex) 
                            ForEach(0 ..< year.count)  i in
                                Text(String(self.year[i])).tag(i)
                            
                        
                        .pickerStyle(SegmentedPickerStyle())
                        
                        Divider()
                        
                            Picker("Month", selection: $monthIndex) 
                                ForEach(0 ..< monthArray.count)  i in
                                    Text(self.monthArray[i]).tag(i)
                                
                            
                            .padding(.bottom, 2)
                        
                    
                    Section(header: Text("What I love about you")) 
                        ForEach(0..<getRange(year: yearIndex, month: monthIndex + indexCheck))  i in
                            NavigationLink(destination: DetailsView()) 
                                Text("Row \(i)")
                            
                        
                    
            
            .listStyle(InsetGroupedListStyle())
            .navigationBarTitle(Text("\(monthArray[monthIndex + indexCheck]) \(String(year[yearIndex]))"))
        
        
    
    func getRange(year: Int, month: Int) -> Int 
        return Calendar.current.range(of: .day, in: .month, for: Calendar.current.date(from: DateComponents(year: year + 2020, month: month + 1))!)!.count
    


struct YearView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

【问题讨论】:

尽可能提供minimal reproducible example。理想情况下,我们只需复制粘贴即可查看有什么问题。 @pawello2222 我按要求添加了最小可重现示例!唯一需要的就是DetailsView(),但我想因为我只是将其保留为默认的SwiftUI文件而不包含它! 【参考方案1】:

我稍微清理了你的代码,使其更具可读性。

这里是ContentView

struct ContentView: View 
    let yearArray = [2020, 2021, 2022]
    let monthArray = DateFormatter().monthSymbols!

    // you don't need to operate on indices, you can use real values
    @State var selectedYear = 2020
    @State var selectedMonth = 1

    // improved readability
    var combinedYearMonth: String 
        "\(monthArray[selectedMonth - 1]) \(selectedYear)"
    

    var body: some View 
        NavigationView 
            List 
                pickerSection
                daySelectionSection
            
            .listStyle(InsetGroupedListStyle())
            .navigationBarTitle(combinedYearMonth)
        
    

负责显示列表部分的部分:

// sections extracted to a private extension (you can still have everything in one `ContentView` struct if you want)
private extension ContentView 
    var pickerSection: some View 
        Section 
            yearPicker
            monthPicker
        
    

    var daySelectionSection: some View 
        Section(header: Text("What I love about you")) 
            ForEach(dayRange, id: \.self)  day in
                NavigationLink(destination: DetailsView()) 
                    Text("Day \(day)")
                
            
        
    

    // create a range of days in the `selectedMonth` for the `selectedYear`
    var dayRange: Range<Int> 
        let dateComponents = DateComponents(year: selectedYear, month: selectedMonth)
        let calendar = Calendar.current
        let date = calendar.date(from: dateComponents)!
        return calendar.range(of: .day, in: .month, for: date)!
    

还有带选择器的部分:

private extension ContentView 
    var yearPicker: some View 
        Picker("Years", selection: $selectedYear) 
            ForEach(yearArray, id: \.self)  year in
                Text(String(year)) // <- no need for `.tag()` if the `id` parameter in `ForEach` is specified
            
        
        .pickerStyle(SegmentedPickerStyle())
    

    var monthPicker: some View 
        Picker("Month", selection: $selectedMonth) 
            ForEach(1...12, id: \.self)  month in
                Text(self.monthArray[month - 1])
            
        
        .padding(.bottom, 2)
    

【讨论】:

天哪,非常感谢!效果很好!并感谢您重新格式化/清理我的代码!老实说,我不知道您可以像这样拆分视图并将变量用作视图,所以感谢您还教了我一些新东西!

以上是关于SwiftUI:如何根据 Picker 的值更新 ForEach 循环的范围的主要内容,如果未能解决你的问题,请参考以下文章

当 Swiftui Picker 滚动其列表时触发的事件

SwiftUI:以编程方式设置 Picker 的值

更新 SwiftUI 导航栏标题

将 SwiftUI Picker 绑定到字典中保存的值

SwiftUI 从另一个视图中捕获 Picker 值

iOS 15 SwiftUI 3 Picker 绑定在更改@State 值后不起作用