Swift中如何合并不同数组模型并在SwiftUI中排序展示?
合并两个Swift结构体模型并在SwiftUI列表中排序展示的问题
问题场景
我尝试合并两个结构体模型,希望在SwiftUI自定义列表中完成合并、排序展示,并根据模型类型跳转对应详情页。单独展示单个列表无异常,但合并后控制台输出Optional([]),视图无法正常启动,不清楚如何正确实现需求。
模型定义
第一个模型:BetModel
struct BetModel: Identifiable, Hashable, BetProtocol { init( id: Int64?, date: Date, team1: String, team2: String, selectedTeam: SelectedTeam, league: String?, amount: NSDecimalNumber, odds: NSDecimalNumber, category: Category, tax: NSDecimalNumber, profit: NSDecimalNumber, isWon: Bool?, betNotificationID: String?, score: NSDecimalNumber? ) { self.id = id self.date = date self.team1 = team1 self.team2 = team2 self.selectedTeam = selectedTeam self.league = league self.amount = amount self.odds = odds self.category = category self.tax = tax self.profit = profit self.isWon = isWon self.betNotificationID = betNotificationID self.score = score } var id: Int64? var date: Date let team1: String let team2: String let selectedTeam: SelectedTeam let league: String? let amount: NSDecimalNumber let odds: NSDecimalNumber let category: Category let tax: NSDecimalNumber let profit: NSDecimalNumber let isWon: Bool? let betNotificationID: String? let score: NSDecimalNumber? } enum SelectedTeam: Int { case team1 case team2 } enum Category: String, CaseIterable { case football case basketball case f1 case tenis case volleyball }
第二个模型:BetslipModel
struct BetslipModel: Identifiable, Hashable, BetProtocol { init( id: Int64?, date: Date, name: String, amount: NSDecimalNumber, odds: NSDecimalNumber, category: Category, tax: NSDecimalNumber, profit: NSDecimalNumber, isWon: Bool?, betNotificationID: String?, score: NSDecimalNumber? ) { self.id = id self.date = date self.name = name self.amount = amount self.odds = odds self.category = category self.tax = tax self.profit = profit self.isWon = isWon self.betNotificationID = betNotificationID self.score = score } var id: Int64? let name: String var date: Date let amount: NSDecimalNumber let odds: NSDecimalNumber let category: Category let tax: NSDecimalNumber let profit: NSDecimalNumber let isWon: Bool? let betNotificationID: String? let score: NSDecimalNumber? }
定义的协议
protocol BetProtocol: Identifiable, Hashable { var id: Int64? { get set } var date: Date { get set } }
ViewModel中的合并尝试
@Published var historyBets: [BetModel]? = [] @Published var betslipHistory: [BetslipModel]? = [] var mergedBets: [any BetProtocol]? { ((historyBets ?? []) + (betslipHistory ?? [])) }
SwiftUI视图代码
LazyVStack(spacing: 12) { ForEach( vm.mergedBets, id: \.id) { bet in if let betModel = bet as? BetModel { NavigationLink( destination: BetDetailsScreen(bet: bet) ) { BetListElement(bet: bet, currency: vm.currency) } } else if let betslipModel = bet as? BetslipModel { NavigationLink( destination: BetslipDetailsScreen(bet: bet) ) { BetslipListElement(bet: bet, currency: vm.currency) } } } }
问题修复与优化方案
1. 修正模型语法错误
原模型中存在无效语法:let team1: "String" 这类写法是错误的,必须改为let team1: String,否则代码无法编译通过,这是合并数组为空的潜在原因之一。
2. 优化ViewModel的合并与排序逻辑
- 避免使用可选数组,直接用非可选空数组初始化,减少可选绑定复杂度:
@Published var historyBets: [BetModel] = [] @Published var betslipHistory: [BetslipModel] = []
- 调整
mergedBets为非可选数组,并添加按日期排序的逻辑(示例为降序):
var mergedBets: [any BetProtocol] { let combined = historyBets + betslipHistory return combined.sorted { $0.date > $1.date } }
3. 修正SwiftUI视图的ForEach使用
处理[any BetProtocol]类型数组时,需明确Hashable标识,同时确保导航链接的泛型参数正确传递:
LazyVStack(spacing: 12) { ForEach(vm.mergedBets, id: \.self) { bet in Group { if let betModel = bet as? BetModel { NavigationLink(destination: BetDetailsScreen(bet: betModel)) { BetListElement(bet: betModel, currency: vm.currency) } } else if let betslipModel = bet as? BetslipModel { NavigationLink(destination: BetslipDetailsScreen(bet: betslipModel)) { BetslipListElement(bet: betslipModel, currency: vm.currency) } } } } }
若\.self无法正常工作,可在协议中添加稳定ID的计算属性:
protocol BetProtocol: Identifiable, Hashable { var id: Int64? { get set } var date: Date { get set } var stableId: Int64 { get } } // 在两个模型中实现该属性 extension BetModel { var stableId: Int64 { id ?? UUID().uuid.int64Value } } extension BetslipModel { var stableId: Int64 { id ?? UUID().uuid.int64Value } }
之后ForEach使用id: \.stableId即可。
4. 确保协议的Hashable一致性
若遇到Hashable相关问题,可在协议中明确要求实现哈希逻辑:
protocol BetProtocol: Identifiable, Hashable { var id: Int64? { get set } var date: Date { get set } func hash(into hasher: inout Hasher) } // 在BetModel中实现 extension BetModel { func hash(into hasher: inout Hasher) { hasher.combine(id) hasher.combine(date) } } // 在BetslipModel中实现 extension BetslipModel { func hash(into hasher: inout Hasher) { hasher.combine(id) hasher.combine(date) } }
内容的提问来源于stack exchange,提问作者lovemyreves
相关产品推荐
相关产品推荐

