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

SwiftUI循环代码过长编译失败,如何传递循环参数至子视图?

解决SwiftUI循环中代码冗长及索引传递问题

核心问题分析

你当前使用多个独立全局数组存储潮汐站数据,通过索引关联数据的方式不仅容易出错,还导致循环内代码过度耦合,超出编译器类型检查的复杂度阈值。解决思路是将分散的数据整合成统一模型,通过子视图接收模型实例替代索引传递,同时用状态管理类统一处理数据加载与更新。


步骤1:重构数据模型

创建包含所有潮汐站属性的结构体,遵循Identifiable协议(无需手动管理ID,方便ForEach遍历):

import Foundation
import MapKit

struct TideStation: Identifiable {
    let id = UUID()
    let siteName: String
    let nextLowTideTime: String
    let nextLowTideHeight: Double
    let nextHighTideTime: String
    let nextHighTideHeight: Double
    let tidalState: String
    let coordinate: CLLocationCoordinate2D
    let observedHeight: Double
    let predictedHeight: Double
    let surge: Double
}

步骤2:用ObservableObject管理数据状态

替换全局数组,创建数据管理类统一处理加载与更新,确保数据变化时视图自动刷新:

import Alamofire
import SwiftyJSON

class TideDataManager: ObservableObject {
    @Published var tideStations: [TideStation] = []
    
    func loadStationData() {
        let generalTideUrl = "http://www.pla.co.uk/hydrographics/ajax/ltoverview.php"
        
        AF.request(generalTideUrl, method: .get).responseJSON { response in
            switch response.result {
            case .success:
                guard let data = response.data,
                      let generalTideResult = try? JSON(data: data),
                      let generalTideArray = generalTideResult["tides"].array else {
                    return
                }
                
                self.tideStations = generalTideArray.compactMap { item in
                    // 处理时间格式
                    func formatTime(_ rawTime: String) -> String {
                        guard rawTime.count >= 11 else { return rawTime }
                        let startIndex = rawTime.index(rawTime.startIndex, offsetBy: 11)
                        return String(rawTime[startIndex...])
                    }
                    
                    guard let latitude = item["latitude"].double,
                          let longitude = item["longitude"].double else {
                        return nil
                    }
                    
                    return TideStation(
                        siteName: item["site_name"].stringValue,
                        nextLowTideTime: formatTime(item["next_lw_time"].stringValue),
                        nextLowTideHeight: item["next_lw_height"].doubleValue,
                        nextHighTideTime: formatTime(item["next_hw_time"].stringValue),
                        nextHighTideHeight: item["next_hw_height"].doubleValue,
                        tidalState: item["tidal_state"].stringValue,
                        coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
                        observedHeight: item["observed_height"].doubleValue,
                        predictedHeight: item["predicted_height"].doubleValue,
                        surge: item["surge_height"].doubleValue
                    )
                }
            case .failure(let error):
                print(error)
            }
        }.resume()
    }
}

步骤3:拆分子视图

将列表项和详情页拆分为独立视图,直接接收TideStation实例,无需传递索引:

列表项视图

struct StationListItemView: View {
    let station: TideStation
    let screenWidth: CGFloat
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 8)
                .strokeBorder(.white.opacity(0.3), lineWidth: 1)
                .background(RoundedRectangle(cornerRadius: 8).fill(.thinMaterial))
                .frame(width: screenWidth - 40, height: 80)
                .overlay {
                    VStack(alignment: .leading) {
                        Spacer()
                        Text(station.siteName)
                            .padding(.leading, 10)
                            .foregroundStyle(LinearGradient(colors: [.secondary, .secondary.opacity(0.8)], startPoint: .leading, endPoint: .trailing))
                        Spacer()
                        Divider()
                        Spacer()
                        HStack {
                            Spacer()
                            Text("High Water: ")
                            Text(station.nextHighTideTime)
                            Spacer()
                            Text("Low Water: ")
                            Text(station.nextLowTideTime)
                            Spacer()
                            if station.tidalState == "Flood" {
                                Image(systemName: "arrow.up").foregroundColor(.green)
                            } else {
                                Image(systemName: "arrow.down").foregroundColor(.red)
                            }
                            Spacer()
                        }
                        Spacer()
                    }
                }
        }
    }
}

详情页视图

struct StationDetailView: View {
    let station: TideStation
    let screenWidth: CGFloat
    @State private var mapRegion: MKCoordinateRegion
    
    init(station: TideStation, screenWidth: CGFloat) {
        self.station = station
        self.screenWidth = screenWidth
        self._mapRegion = State(initialValue: MKCoordinateRegion(
            center: station.coordinate,
            span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
        ))
    }
    
    var body: some View {
        VStack(alignment: .center) {
            Text("\(station.surge)")
            Text(station.siteName)
                .font(.largeTitle)
            
            Map(coordinateRegion: $mapRegion, annotationItems: [station]) { station in
                MapMarker(coordinate: station.coordinate)
            }
            .frame(width: screenWidth, height: 250)
            
            HStack {
                RoundedRectangle(cornerRadius: 10)
                    .foregroundStyle(.thinMaterial)
                    .frame(width: 150, height: 120)
                    .overlay {
                        VStack(alignment: .leading , spacing: 10) {
                            Text("Next Low Tide:")
                            HStack {Text("Time: "); Text(station.nextLowTideTime)}.foregroundColor(.secondary)
                            HStack {Text("Height: "); Text("\(station.nextLowTideHeight)"); Text("m")}.foregroundColor(.secondary)
                        }
                    }
                
                RoundedRectangle(cornerRadius: 10)
                    .foregroundStyle(.thinMaterial)
                    .frame(width: 150, height: 120)
                    .overlay {
                        VStack(alignment: .leading, spacing: 10) {
                            Text("Next High Tide:")
                            HStack {Text("Time: "); Text(station.nextHighTideTime)}.foregroundColor(.secondary)
                            HStack {Text("Height: "); Text("\(station.nextHighTideHeight)"); Text("m")}.foregroundColor(.secondary)
                        }
                    }
            }
        }
    }
}

步骤4:修改主视图与列表视图

注入数据管理器,简化循环逻辑:

主视图

struct GeneralTidesView: View {
    @StateObject private var dataManager = TideDataManager()
    private let screenWidth = UIScreen.main.bounds.width
    
    var body: some View {
        NavigationStack {
            ScrollView(.vertical, showsIndicators: false) {
                VStack (alignment: .center) {
                    Spacer()
                    
                    Image(systemName: "chart.xyaxis.line")
                        .font(.largeTitle)
                        .frame(width: 300, height: 300, alignment: .center)
                    
                    Spacer()
                    
                    Text("More Stations")
                        .font(.title3)
                        .foregroundStyle(LinearGradient(colors: [.primary, .secondary], startPoint: .topLeading, endPoint: .bottomTrailing))
                        .frame(width: screenWidth, height: 40)
                        .background(.thickMaterial)
                    
                    Spacer()
                    
                    Divider()
                    
                    Spacer()
                    
                    StationList(tideStations: dataManager.tideStations, screenWidth: screenWidth)
                }
            }
            .navigationBarTitle("Tides")
            .navigationBarTitleDisplayMode(.inline)
            .onAppear {
                dataManager.loadStationData()
            }
        }
    }
}

列表视图

struct StationList: View {
    let tideStations: [TideStation]
    let screenWidth: CGFloat
    
    var body: some View {
        ForEach(tideStations) { station in
            NavigationLink(destination: StationDetailView(station: station, screenWidth: screenWidth)) {
                StationListItemView(station: station, screenWidth: screenWidth)
            }
        }
    }
}

关键优化点

  • 用单个模型数组替代多个独立数组,彻底避免索引关联错误
  • 子视图直接接收模型实例,消除索引传递需求,简化代码结构
  • 用ObservableObject管理数据,确保数据更新时视图自动刷新
  • 拆分视图后,编译器类型检查复杂度大幅降低,解决编译卡顿问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 22:24:27