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

SwiftUI:为Rick and Morty角色列表加NavigationLink并获取API位置信息

解决方案

问题1:为角色添加有效的NavigationLink跳转详情页

你当前代码已使用NavigationLink,但传递给DetailView的是空白Result实例,需修改为传递当前遍历的真实角色数据:

  1. 修正CharacterList中的NavigationLink代码:
NavigationLink(destination: DetailView(character: character.element)) {
    // 原CharacterView代码保持不变
}
  1. 优化DetailView的参数定义,使其接收真实角色数据:
struct DetailView: View {
    var character: Result // 变量名改为character,语义更清晰
    
    var body: some View {
        VStack(spacing: 20) {
            AsyncImage(url: URL(string: character.image)) { image in
                image.resizable()
                    .scaledToFit()
            } placeholder: {
                ProgressView()
            }
            .frame(width: 200, height: 200)
            .cornerRadius(10)
            
            Text(character.name)
                .font(.title)
                .fontWeight(.bold)
            
            // 预留位置信息展示位,后续补充
            Text("位置:\(character.location.name)")
                .font(.headline)
        }
        .padding()
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView(character: Result(id: 1, name: "Rick Sanchez", status: "Alive", image: "", species: "Human", gender: "Male", location: Location(name: "Earth (C-137)", url: "")))
    }
}

问题2:获取角色的位置信息

第一步:更新数据模型

根据API返回结构,角色位置是嵌套对象,需在Result结构体中添加对应的Location模型:

修改RickandMorty.swift:

struct RickandMorty: Codable {
    let info: Info
    let results: [Result]
}

struct Info: Codable {
    let count, pages: Int
    let next: String?
    let prev: String?
}

struct Location: Codable {
    let name: String
    let url: String
}

struct Result: Codable {
    let id: Int
    let name: String
    let status: String
    let image: String
    let species: String
    let gender: String
    let location: Location // 添加位置字段
}

第二步:展示位置基础信息

现在可直接在DetailView中通过character.location.name获取并展示角色的位置名称,如上述DetailView代码所示。

第三步:(可选)获取位置详细信息

若需通过location.url获取更完整的位置数据,可在DetailView中添加网络请求逻辑:

  1. 创建处理位置数据的LocationData类:
import Foundation
import Combine

class LocationData: ObservableObject {
    private var cancellables = Set<AnyCancellable>()
    @Published var locationDetails: LocationFull?
    @Published var isLoading = false
    
    struct LocationFull: Codable {
        let id: Int
        let name: String
        let type: String
        let dimension: String
        let residents: [String]
    }
    
    func loadLocation(from urlString: String) {
        guard let url = URL(string: urlString) else { return }
        isLoading = true
        
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: LocationFull.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { [weak self] completion in
                self?.isLoading = false
                if case .failure(let error) = completion {
                    print("加载位置失败:\(error)")
                }
            }, receiveValue: { [weak self] location in
                self?.locationDetails = location
            })
            .store(in: &cancellables)
    }
}
  1. 修改DetailView使用该类加载详情:
struct DetailView: View {
    var character: Result
    @StateObject private var locationData = LocationData()
    
    var body: some View {
        ScrollView {
            VStack(spacing: 20) {
                AsyncImage(url: URL(string: character.image)) { image in
                    image.resizable()
                        .scaledToFit()
                } placeholder: {
                    ProgressView()
                }
                .frame(width: 200, height: 200)
                .cornerRadius(10)
                
                Text(character.name)
                    .font(.title)
                    .fontWeight(.bold)
                
                Text("当前位置:\(character.location.name)")
                    .font(.headline)
                
                if locationData.isLoading {
                    ProgressView()
                } else if let details = locationData.locationDetails {
                    VStack(alignment: .leading, spacing: 10) {
                        Text("位置详情")
                            .font(.title2)
                            .fontWeight(.bold)
                        
                        Text("类型:\(details.type)")
                        Text("维度:\(details.dimension)")
                        Text("居民数量:\(details.residents.count)")
                    }
                    .padding()
                    .background(Color.gray.opacity(0.1))
                    .cornerRadius(10)
                }
            }
            .padding()
        }
        .onAppear {
            locationData.loadLocation(from: character.location.url)
        }
    }
}

额外优化建议

针对你标记的// MARK: - Fix code duplicates,可将标签样式抽为通用View:

struct TagView: View {
    let text: String
    let backgroundColor: Color
    
    var body: some View {
        Text(text)
            .fontWeight(.bold)
            .foregroundColor(.white)
            .padding(.horizontal, 5)
            .background(backgroundColor)
            .cornerRadius(3)
            .lineLimit(1)
    }
}

然后在CharacterView的HStack中替换为:

HStack {
    TagView(text: status, backgroundColor: stats)
    TagView(text: species, backgroundColor: spec)
    TagView(text: gender, backgroundColor: gndr)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 01:15:37