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

SwiftUI中Navigation View与Alert Box的功能实现问题

SwiftUI卡片跳转与Alert触发问题解决

问题描述

我有若干卡片,要求只有在同时满足ifButtonDisabled和ifLevelUnlocked两个条件时,点击卡片才跳转到目标视图;如果条件不满足,就根据未满足的条件弹出对应的提示弹窗。

目前遇到两个问题:

  • 条件不满足时,NavigationView会跳转到EmptyView,我需要此时完全不执行跳转;
  • 无法根据不同的未满足条件触发对应的两个Alert。

另外想请教有没有更优雅的实现方式?

以下是当前代码:

ForEach(Array(indexElementsArray.enumerated()), id: \.0) {index, item in
    NavigationLink {
        if coreDataHelper.checkIfButtonDisabled(levelName: "Level\(index + 1)") && coreDataHelper.checkIfLevelUnlocked(levelName: "Level\(index + 1)"){
            ChapterListView(heading: "Lesson \(index+1) - \(item.level)", index: index)
        }else{
            EmptyView()
        }
    } label: {
        IndexCard(lessonNumber: index, heading: item.level, image: item.image, bgColor: item.bgColor)
    }
    .simultaneousGesture(
        TapGesture().onEnded({
            if !coreDataHelper.checkIfButtonDisabled(levelName: "Level\(index + 1)"){
                if !coreDataHelper.checkIfLevelUnlocked(levelName: "Level\(index + 1)"){
                    showAlertToCompletePriorLevels.toggle()
                }
            }else{
                showAlertToDownload.toggle()
            }
        })
    )
}

解决方案与优化代码

核心问题分析

原代码的问题在于:

  1. NavigationLink会无条件触发跳转,只是用EmptyView作为“无效跳转”的占位,这不符合需求;
  2. 手势的条件判断逻辑嵌套混乱,导致部分场景无法正确触发对应Alert。

修复后的代码(兼容iOS 13+)

// 在视图结构体中添加状态变量
@State private var shouldNavigate = false
@State private var showDownloadAlert = false
@State private var showCompletePriorAlert = false

// ... 其他视图代码 ...

ForEach(Array(indexElementsArray.enumerated()), id: \.0) { index, item in
    // 提前计算重复用到的变量,减少重复调用和字符串拼接
    let levelName = "Level\(index + 1)"
    let isButtonEnabled = coreDataHelper.checkIfButtonDisabled(levelName: levelName)
    let isLevelUnlocked = coreDataHelper.checkIfLevelUnlocked(levelName: levelName)
    
    NavigationLink(
        destination: ChapterListView(heading: "Lesson \(index+1) - \(item.level)", index: index),
        isActive: $shouldNavigate
    ) {
        IndexCard(lessonNumber: index, heading: item.level, image: item.image, bgColor: item.bgColor)
    }
    .simultaneousGesture(TapGesture().onEnded {
        // 每次点击先重置跳转状态
        shouldNavigate = false
        
        // 按顺序判断条件,逻辑更清晰
        guard isButtonEnabled else {
            showDownloadAlert = true
            return
        }
        
        guard isLevelUnlocked else {
            showCompletePriorAlert = true
            return
        }
        
        // 两个条件都满足,才激活跳转
        shouldNavigate = true
    })
}
// 绑定两个Alert
.alert("请先下载内容", isPresented: $showDownloadAlert) {
    Button("确认", role: .cancel) {}
}
.alert("请先完成前置关卡", isPresented: $showCompletePriorAlert) {
    Button("确认", role: .cancel) {}
}

iOS 16+ 更优雅的实现(使用NavigationStack)

如果你的APP适配iOS 16及以上,推荐使用NavigationStack的navigationDestination API,写法更简洁,无需手动管理跳转状态:

@State private var selectedLessonIndex: Int? = nil
@State private var showDownloadAlert = false
@State private var showCompletePriorAlert = false

// ... 其他视图代码 ...

NavigationStack {
    ForEach(Array(indexElementsArray.enumerated()), id: \.0) { index, item in
        let levelName = "Level\(index + 1)"
        let isButtonEnabled = coreDataHelper.checkIfButtonDisabled(levelName: levelName)
        let isLevelUnlocked = coreDataHelper.checkIfLevelUnlocked(levelName: levelName)
        
        IndexCard(lessonNumber: index, heading: item.level, image: item.image, bgColor: item.bgColor)
            .onTapGesture {
                guard isButtonEnabled else {
                    showDownloadAlert = true
                    return
                }
                
                guard isLevelUnlocked else {
                    showCompletePriorAlert = true
                    return
                }
                
                // 选中对应关卡,触发跳转
                selectedLessonIndex = index
            }
    }
    // 绑定跳转目标
    .navigationDestination(for: Int.self) { index in
        let item = indexElementsArray[index]
        ChapterListView(heading: "Lesson \(index+1) - \(item.level)", index: index)
    }
    // 绑定Alert
    .alert("请先下载内容", isPresented: $showDownloadAlert) {
        Button("确认", role: .cancel) {}
    }
    .alert("请先完成前置关卡", isPresented: $showCompletePriorAlert) {
        Button("确认", role: .cancel) {}
    }
}

优化点说明

  • 提前计算levelName、isButtonEnabled、isLevelUnlocked,避免重复调用CoreData方法和字符串拼接,提升性能和可读性;
  • 用guard语句替代嵌套if,逻辑更线性,不易出错;
  • 将跳转控制和Alert触发逻辑分离,代码结构更清晰;
  • iOS 16+版本直接使用navigationDestination,无需手动管理NavigationLink的激活状态,代码更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 23:26:01