You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift应用Menu Picker首次切换河流数据匹配异常问题

Swift Menu Picker首次切换时仍使用默认河流ID的问题解决

问题描述

我正在开发一款Swift应用,用Menu Picker提供不同河流的选择选项。现在遇到的问题是:打开应用后首次切换其他河流时,Picker仍会使用默认索引(0)的河流ID获取数据。

比如打开应用默认显示Howes Valley,能正确获取水位数据,但选择其他选项时,还是用Howes Valley的ID“212021”,而不是所选河流的ID。

相关代码

河流数据定义

let rivers: [River] = [
    River(id: "212021", name: "McDonald River @ Howes Valley"),
    River(id: "212461", name: "Hawkesbury River @ Leetsvale"),
    River(id: "212290", name: "Colo River @ Upper Colo")
]
GroupBox(label: Label("River Data", systemImage: "waveform.path.ecg")
                .font(.title)
                .foregroundColor(.blue)
                .padding(.bottom, 8)
            ) {
                
                Picker(selection: $selectedRiverIndex, label: Text("Select a river")) {
                    ForEach(0..<rivers.count, id: \.self) { index in
                        Text(rivers[index].name)
                    }
                }
                .pickerStyle(MenuPickerStyle())
                .onChange(of: selectedRiverIndex) { newIndex, _ in
                    isLoading = true // Show loading indicator
                    Task {
                        let newRiverIndex = newIndex
                        await riverModel.fetchRiverHeightData(for: rivers[newRiverIndex].id)
                        isLoading = false // Hide loading indicator after data is fetched
                    }
                }
              
              // River data details view
                VStack(alignment: .leading, spacing: 8) {
                    ForEach(riverModel.riverData, id: \.time) { riverValue in
                        VStack(alignment: .leading) {
                            if let convertedDate = convertDateString(riverValue.time) {
                                Text("\(riverValue.v)m")
                                    .font(.headline)
                                Text("Last Updated:")
                                    .font(.subheadline)
                                    .foregroundColor(.secondary)
                                Text("\(convertedDate.date) at \(convertedDate.time)")
                                    .font(.subheadline)
                            }
                        }
                        .padding(.vertical, 4)
                    }
                }
            }

应用启动默认逻辑

// Fetch river details and river data when the view appears        
.onAppear {
            Task {
                let selectedRiverID = "212021" // Set the initial selected river ID
                await riverModel.fetchRiverHeightData(for: selectedRiverID)
                isLoading = false // Hide loading indicator after data is fetched
            }
            
        }

问题原因与修复方案

核心原因

你没有在视图初始化时同步selectedRiverIndex的默认值与硬编码的默认河流ID,导致Picker的选择状态和实际请求的ID出现不一致;另外如果selectedRiverIndex没有被声明为@State/@Binding属性,状态更新也无法被SwiftUI正确捕获。

修复步骤

  1. 确保selectedRiverIndex是状态变量
    在视图中显式声明带默认值的状态变量:

    @State private var selectedRiverIndex = 0 // 默认对应索引0的河流
    

    如果使用@Binding,确保父视图也正确初始化该值。

  2. 同步onAppear中的默认请求逻辑
    去掉硬编码的ID,改用selectedRiverIndex关联的河流ID,保持状态统一:

    .onAppear {
        Task {
            await riverModel.fetchRiverHeightData(for: rivers[selectedRiverIndex].id)
            isLoading = false
        }
    }
    
  3. 优化onChange的状态更新逻辑
    确保UI状态更新在主线程执行(SwiftUI的Task默认会回到主线程,但显式标注更严谨):

    .onChange(of: selectedRiverIndex) { newIndex, _ in
        Task {
            await MainActor.run {
                isLoading = true
            }
            await riverModel.fetchRiverHeightData(for: rivers[newIndex].id)
            await MainActor.run {
                isLoading = false
            }
        }
    }
    
  4. 可选:直接绑定River对象(更优雅的方案)
    避免使用索引,直接绑定River实例减少出错概率:

    • 声明状态变量并初始化:
      @State private var selectedRiver: River
      
      init() {
          _selectedRiver = State(initialValue: rivers[0])
      }
      
    • 修改Picker实现:
      Picker(selection: $selectedRiver, label: Text("Select a river")) {
          ForEach(rivers, id: \.id) { river in
              Text(river.name)
          }
      }
      
    • 更新数据请求逻辑:
      .onChange(of: selectedRiver) { newRiver in
          Task {
              await MainActor.run { isLoading = true }
              await riverModel.fetchRiverHeightData(for: newRiver.id)
              await MainActor.run { isLoading = false }
          }
      }
      .onAppear {
          Task {
              await riverModel.fetchRiverHeightData(for: selectedRiver.id)
              isLoading = false
          }
      }
      

内容的提问来源于stack exchange,提问作者Tristan N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 00:35:31