通过Navigation Link传递Binding导致应用崩溃求助
导航栈跳转至具体食材视图时应用冻结无日志的问题
导航栈结构
- 所有食谱 > 具体食谱 > 食材列表 > 具体食材
相关代码
【所有食谱视图】
struct RecipesScreen: View { @EnvironmentObject var sessionData: SessionData var body: some View { NavigationStack { List() { ForEach($sessionData.familyData.familyRecipes, id: \.self) { $recipe in NavigationLink { EditRecipeScreen(recipe: $recipe) } label: { Text("\(recipe.originalRecipe.title)") } } } } } }
【具体食谱视图】
struct EditRecipeScreen: View { @Binding var recipe: FamilyRecipe @EnvironmentObject var sessionData: SessionData var body: some View { List { // title TextField("Title", text: $recipe.originalRecipe.title) // ingredients > NavigationLink { IngredientsList(ingredients: $recipe.originalRecipe.ingredients) } label: { Text("Ingredients") } // cook > NavigationLink { InstructionsNavigation(instructions: $recipe.originalRecipe.cookInstructions, minutes: $recipe.originalRecipe.minutesToCook, type: "cook") } label: { Text("Cooking steps") } } } }
【食材列表视图】
struct IngredientsList: View { @EnvironmentObject var sessionData: SessionData @Binding var ingredients: [RecipeIngredient] var body: some View { List { ForEach($ingredients, id: \.ingredient.id) { $recipeIngredient in NavigationLink { IngredientView(recipeIngredient: $recipeIngredient) } label: { HStack { Text(recipeIngredient.ingredient.displayName) Spacer() Text("\(recipeIngredient.amount.removeZerosFromEnd()) \(recipeIngredient.measure.description)") } } } } } }
【具体食材视图】
struct IngredientView: View { @Binding var recipeIngredient: RecipeIngredient var body: some View { Text("\(recipeIngredient.ingredient.displayName)") Text(recipeIngredient.ingredient.measure.description) } }
问题现象
代码简化版功能基本正常,但点击导航链接进入具体食材视图时,手机端应用冻结(无崩溃退出,Xcode无日志输出):
- 若将具体食材视图中的文本直接放在导航链接的label内,无异常;
- 若将具体食材视图的
recipeIngredient改为@State并以非绑定方式传入,也能正常运行,但无法更新原始食材数据,因此需要解决绑定方式下的冻结问题。
解决方案尝试
1. 检查RecipeIngredient的Equatable实现
SwiftUI的绑定和ForEach依赖稳定的状态标识,若RecipeIngredient未正确实现Equatable,可能导致状态追踪异常。确保RecipeIngredient及其内部类型都实现协议:
struct RecipeIngredient: Equatable { static func == (lhs: Self, rhs: Self) -> Bool { lhs.ingredient.id == rhs.ingredient.id && lhs.amount == rhs.amount && lhs.measure == rhs.measure } var ingredient: Ingredient var amount: Double var measure: MeasureType } struct Ingredient: Equatable, Identifiable { let id: UUID var displayName: String var measure: MeasureType }
2. 调整导航链接的绑定传递方式
改用NavigationLink(value:)配合navigationDestination的方式传递绑定,避免在闭包中直接创建绑定导致的状态追踪混乱:
struct IngredientsList: View { @EnvironmentObject var sessionData: SessionData @Binding var ingredients: [RecipeIngredient] var body: some View { List { ForEach($ingredients, id: \.ingredient.id) { $recipeIngredient in NavigationLink(value: recipeIngredient) { HStack { Text(recipeIngredient.ingredient.displayName) Spacer() Text("\(recipeIngredient.amount.removeZerosFromEnd()) \(recipeIngredient.measure.description)") } } } } .navigationDestination(for: RecipeIngredient.self) { targetIngredient in if let index = ingredients.firstIndex(where: { $0.ingredient.id == targetIngredient.ingredient.id }) { IngredientView(recipeIngredient: $ingredients[index]) } else { Text("Ingredient not found") } } } }
3. 验证removeZerosFromEnd()方法安全性
自定义的removeZerosFromEnd()方法若存在逻辑漏洞(如无限循环),可能导致线程阻塞。替换为更安全的实现:
extension Double { func removeZerosFromEnd() -> String { let formatter = NumberFormatter() formatter.minimumFractionDigits = 0 formatter.maximumFractionDigits = 2 return formatter.string(from: NSNumber(value: self)) ?? "\(self)" } }
内容的提问来源于stack exchange,提问作者Fiona Kate
相关产品推荐
相关产品推荐

