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

如何在Swift中将API请求的数据存入列表并在ContentView展示

解决SwiftUI中展示网球选手排名数据的问题

要把API获取的网球选手数据展示在SwiftUI列表中,核心是通过状态管理让视图响应数据变化,以下是具体实现步骤:

1. 创建数据管理ViewModel

首先创建一个遵循ObservableObject的ViewModel类,用来处理API请求和数据状态,这样数据更新时视图会自动刷新:

import Foundation

class RankingsViewModel: ObservableObject {
    // 存储选手排名数据,@Published修饰会自动通知视图更新
    @Published var rankings: [Ranking] = []
    // 加载状态,用于显示加载提示
    @Published var isLoading = false
    // 错误信息,用于展示请求失败提示
    @Published var errorMessage: String?
    
    private let apiURL = "https://tennisapi1.p.rapidapi.com/api/tennis/rankings/atp/?rapidapi-key=APIKEY"
    
    func fetchRankings() {
        isLoading = true
        errorMessage = nil
        
        let task = URLSession.shared.dataTask(with: URL(string: apiURL)!) { [weak self] data, response, error in
            DispatchQueue.main.async {
                self?.isLoading = false
                
                guard let data = data, error == nil else {
                    self?.errorMessage = "请求失败,请检查网络"
                    return
                }
                
                do {
                    let result = try JSONDecoder().decode(RankingsResponse.self, from: data)
                    self?.rankings = result.rankings
                } catch {
                    self?.errorMessage = "数据解析失败:\(error.localizedDescription)"
                }
            }
        }
        task.resume()
    }
}

// 保留你原来的Codable结构体
// MARK: - RankingsResponse
struct RankingsResponse: Codable {
    let rankings: [Ranking]
    let updatedAtTimestamp: Int
}

// MARK: - Ranking
struct Ranking: Codable {
    let bestRanking: Int
    let bestRankingDateTimestamp: Int?
    let country: RankingCountry
    let id, points, previousPoints, previousRanking: Int
    let ranking: Int
    let rowName: String
    let team: Team
    let tournamentsPlayed, type: Int
}

// MARK: - RankingCountry
struct RankingCountry: Codable {
}

// MARK: - Team
struct Team: Codable {
    let country: TeamCountry
    let disabled: Bool?
    let gender: Gender
    let id: Int
    let name, nameCode: String
    let national: Bool
    let ranking: Int
    let shortName, slug: String
    let type, userCount: Int
}

// MARK: - TeamCountry
struct TeamCountry: Codable {
    let alpha2, name: String
}

enum Gender: String, Codable {
    case m = "M"
}

2. 修改ContentView展示数据

在ContentView中使用ViewModel,通过@StateObject实例化(iOS14+),然后用List遍历数据,同时处理加载、错误和空数据的状态:

import SwiftUI

struct ContentView: View {
    // 持有ViewModel实例,StateObject确保视图生命周期内唯一
    @StateObject private var viewModel = RankingsViewModel()
    
    var body: some View {
        NavigationStack { // iOS16+用NavigationStack,之前版本用NavigationView
            VStack(spacing: 16) {
                Button("获取排名") {
                    viewModel.fetchRankings()
                }
                .buttonStyle(.borderedProminent)
                
                // 加载状态提示
                if viewModel.isLoading {
                    ProgressView("加载中...")
                }
                // 错误提示
                else if let error = viewModel.errorMessage {
                    Text(error)
                        .foregroundColor(.red)
                        .multilineTextAlignment(.center)
                        .padding()
                }
                // 数据列表
                else {
                    List(viewModel.rankings) { ranking in
                        HStack(alignment: .top, spacing: 12) {
                            // 排名数字
                            Text("\(ranking.ranking)")
                                .font(.title)
                                .fontWeight(.bold)
                                .foregroundColor(.gray)
                            
                            VStack(alignment: .leading, spacing: 4) {
                                // 选手姓名
                                Text(ranking.rowName)
                                    .font(.headline)
                                // 所属国家
                                Text(ranking.team.country.name)
                                    .font(.subheadline)
                                    .foregroundColor(.secondary)
                                // 积分
                                Text("积分: \(ranking.points)")
                                    .font(.caption)
                            }
                        }
                    }
                    .listStyle(.plain)
                }
            }
            .navigationTitle("ATP排名")
            .padding()
        }
    }
}

// 让Ranking遵循Identifiable,这样List可以直接遍历
extension Ranking: Identifiable {
    var id: Int { self.id } // 用结构体里已有的id字段作为唯一标识
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

关键修改说明

  • ViewModel状态管理:用@Published修饰数据和状态,确保数据变化时视图自动刷新;API请求完成后切换到主线程更新UI,避免线程问题。
  • 视图状态处理:分别展示加载中、错误、数据列表三种状态,提升用户体验。
  • Identifiable扩展:让Ranking遵循Identifiable,List可以直接使用数组而无需指定id参数。
  • 视图结构调整:修正了原代码中ScrollView嵌套NavigationView的错误,使用正确的导航容器层级。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 10:46:17