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

如何在SwiftUI单个List行中嵌入多个NavigationLink?解决苹果提醒App风格区块导航报错问题

解决iOS15 + Xcode13中复刻提醒App区块样式的导航问题

我帮你分析下问题所在:你当前的代码把NavigationLink嵌套在Blocks视图的ZStack中,而在iOS15的InsetGroupedListStyle列表环境下,List会对直接子层级的NavigationLink做特殊处理,嵌套在自定义容器(比如VStack/HStack/ZStack)里的NavigationLink会和List的内部导航逻辑冲突,导致导航状态混乱甚至应用异常。

下面给你两种可行的解决方案,你可以根据需求选择:

方案一:编程式导航(推荐,灵活性更高)

这种方式通过绑定状态变量来控制导航,避免List对嵌套NavigationLink的错误处理:

步骤1:修改ContentView,添加导航状态变量

在ContentView里添加4个@State变量,分别控制4个区块的导航状态:

struct ContentView: View {
    // 控制每个区块的导航状态
    @State private var showBlock1 = false
    @State private var showBlock2 = false
    @State private var showBlock3 = false
    @State private var showBlock4 = false
    
    var body: some View {
        NavigationView {
            List {
                // ... 原有代码的其他Section
            }
            .listStyle(InsetGroupedListStyle())
            .navigationBarTitle(Text("Catalogue"), displayMode: .large)
            // 添加隐藏的导航链接
            .background(
                Group {
                    NavigationLink(destination: Text("1"), isActive: $showBlock1) { EmptyView() }
                    NavigationLink(destination: Text("2"), isActive: $showBlock2) { EmptyView() }
                    NavigationLink(destination: Text("3"), isActive: $showBlock3) { EmptyView() }
                    NavigationLink(destination: Text("4"), isActive: $showBlock4) { EmptyView() }
                }
                .opacity(0) // 隐藏导航链接本身
            )
        }
    }
}

步骤2:修改Blocks视图,添加点击回调

把Blocks改成接受一个点击回调,移除内部的NavigationLink:

struct Blocks: View {
    let title: String
    let onTap: () -> Void // 点击回调
    
    var body: some View {
        ZStack {
            // 给圆角矩形添加填充色,否则区块会看不见
            RoundedRectangle(cornerRadius: 12, style: .continuous)
                .fill(Color.blue)
            Image(systemName: "\(title).circle.fill")
                .foregroundColor(.white)
                .imageScale(.large)
        }
        .onTapGesture(perform: onTap) // 绑定点击事件
    }
}

步骤3:更新Block Section的调用

在调用Blocks时传入对应的导航状态切换逻辑:

Section(header: Text("Block Section")) {
    VStack (spacing: 20) {
        HStack (spacing: 20) {
            Blocks(title: "1", onTap: { showBlock1 = true })
            Blocks(title: "2", onTap: { showBlock2 = true })
        }
        HStack (spacing: 20) {
            Blocks(title: "3", onTap: { showBlock3 = true })
            Blocks(title: "4", onTap: { showBlock4 = true })
        }
    }
    .foregroundColor(Color.blue)
    .frame(height: 160)
    .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
    .listRowBackground(Color.clear)
}

方案二:直接用NavigationLink包装Blocks(更简单)

如果你的导航逻辑比较简单,可以直接把Blocks放在NavigationLink内部,同时去掉默认的按钮样式:

步骤1:简化Blocks视图

移除内部的NavigationLink,保留样式部分:

struct Blocks: View {
    let title: String
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 12, style: .continuous)
                .fill(Color.blue)
            Image(systemName: "\(title).circle.fill")
                .foregroundColor(.white)
                .imageScale(.large)
        }
    }
}

步骤2:更新Block Section的布局

把每个Blocks用NavigationLink包裹,并添加PlainButtonStyle去掉默认的列表项样式:

Section(header: Text("Block Section")) {
    VStack (spacing: 20) {
        HStack (spacing: 20) {
            NavigationLink(destination: Text("1")) {
                Blocks(title: "1")
            }
            .buttonStyle(PlainButtonStyle()) // 移除默认导航链接样式
            
            NavigationLink(destination: Text("2")) {
                Blocks(title: "2")
            }
            .buttonStyle(PlainButtonStyle())
        }
        HStack (spacing: 20) {
            NavigationLink(destination: Text("3")) {
                Blocks(title: "3")
            }
            .buttonStyle(PlainButtonStyle())
            
            NavigationLink(destination: Text("4")) {
                Blocks(title: "4")
            }
            .buttonStyle(PlainButtonStyle())
        }
    }
    .foregroundColor(Color.blue)
    .frame(height: 160)
    .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
    .listRowBackground(Color.clear)
}

额外提示

  • 原来的代码中RoundedRectangle没有设置填充色,导致区块是透明的,我在两种方案里都添加了.fill(Color.blue),你可以根据需要修改颜色。
  • 在iOS15的List中,尽量避免在自定义容器(如VStack、HStack)中嵌套NavigationLink,这是导致你导航异常的核心原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 16:32:51