SwiftUI:为Rick and Morty角色列表加NavigationLink并获取API位置信息
解决方案
问题1:为角色添加有效的NavigationLink跳转详情页
你当前代码已使用NavigationLink,但传递给DetailView的是空白Result实例,需修改为传递当前遍历的真实角色数据:
- 修正
CharacterList中的NavigationLink代码:
NavigationLink(destination: DetailView(character: character.element)) { // 原CharacterView代码保持不变 }
- 优化
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中添加网络请求逻辑:
- 创建处理位置数据的
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) } }
- 修改
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
相关产品推荐
相关产品推荐

