SwiftUI中如何在ForEach第4项点击时跳转付费墙页面
SwiftUI ForEach 内容权限控制实现方案
核心逻辑梳理
要实现需求,关键是获取元素在数组中的索引,结合用户订阅状态做跳转判断:
- 已订阅高级权限:所有项直接跳转详情页
- 未订阅:前3项(索引0、1、2)正常跳转,第4项及以后(索引≥3)跳转付费墙
修改后的完整代码
struct ContentView: View { @Environment(ViewModel.self) var viewModel @Environment(PaywallViewModel.self) var paywallViewModel @Bindable var vm: ViewModel init() { self.vm = ViewModel() // 根据实际ViewModel初始化逻辑调整 } var body: some View { NavigationStack { GeometryReader { geo in ScrollView(showsIndicators: false) { // 改用带索引的枚举形式遍历数组 ForEach(Array(viewModel.filteredDinos.enumerated()), id: \.element.id) { index, predator in VStack { NavigationLink(destination: getDestination(for: predator, at: index)) { withAnimation { PredatorCard(predator: predator) // 给非订阅用户的受限项添加锁标识 .overlay( Group { if !paywallViewModel.hasPremiumSubscription && index >= 3 { Image(systemName: "lock") .padding(8) .background(Color.black.opacity(0.7)) .foregroundColor(.white) .cornerRadius(8) .position(x: geo.size.width - 20, y: 20) } } ) } } } .frame(width: geo.size.width) } } .navigationTitle("Jurassic Journey") .searchable(text: $vm.searchText) .autocorrectionDisabled() .toolbar { ToolbarItem(placement: .topBarLeading) { Button(action: { withAnimation { viewModel.alphabetical.toggle() } }) { Image(systemName: viewModel.alphabetical ? "film" : "textformat") .symbolEffect(.bounce, value: viewModel.alphabetical) } } ToolbarItem(placement: .topBarTrailing) { Menu { Picker("Filter", selection: $vm.currentTypeSelection.animation()) { ForEach(PredatorType.allCases) { type in Label(type.rawValue.capitalized, systemImage: type.icon) } } } label: { Image(systemName: "slider.horizontal.3") } } } } } .navigationSplitViewStyle(.automatic) .preferredColorScheme(.dark) } // 抽离跳转逻辑,简化body代码 private func getDestination(for predator: Predator, at index: Int) -> some View { if paywallViewModel.hasPremiumSubscription { return AnyView(PredatorDetail(predator: predator, position: .camera(MapCamera(centerCoordinate: predator.location, distance: 2000)))) } else { return index < 3 ? AnyView(PredatorDetail(predator: predator, position: .camera(MapCamera(centerCoordinate: predator.location, distance: 2000)))) : AnyView(PaywallView()) // 替换为你的付费墙页面 } } }
关键修改点说明
- 带索引遍历:通过
Array(viewModel.filteredDinos.enumerated())将数组转为带索引的元组集合,直接获取元素位置index - 条件跳转:把跳转目标判断逻辑抽离到
getDestination方法,根据订阅状态和索引返回对应页面 - 视觉提示:给非订阅用户的第4项及以后卡片添加锁图标,明确告知内容需要订阅解锁
- 语法修正:将
@Bindable属性移到body外,符合SwiftUI语法规范
注意事项
- 确保
PaywallViewModel中存在hasPremiumSubscription属性,用于判断用户订阅状态 - 将代码中的
PaywallView()替换为你实际的付费墙页面结构体 - 若
ViewModel初始化需要依赖环境,调整init方法实现逻辑
内容的提问来源于stack exchange,提问作者AlTheBeast
相关产品推荐
相关产品推荐

