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

SwiftUI可复用TextPopUp视图问题:绑定导致弹窗内容异常求助

问题分析与解决方案

你的问题根源很明确:所有TextPopUpView实例共享了同一个showPopover的Binding状态。当你长按任意组件时,这个共享的布尔值变为true,所有绑定它的popover都会尝试触发显示,但SwiftUI的渲染逻辑会优先展示最后一个被声明的popover内容——这就是为什么你每次都只看到ELEMENT的描述。

下面给你两种最实用的解决思路:


方案一:让每个弹窗拥有独立的内部状态(推荐,更优雅)

重构TextPopUpView,把外部传入的Binding改成组件自己管理的@State,这样每个实例的弹窗状态完全独立,不会互相干扰:

struct TextPopUpView: View { 
    @EnvironmentObject var oracleViewModel: OracleViewModel 
    // 将外部Binding替换为内部私有State,每个实例单独控制
    @State private var showPopover: Bool = false
    var displayedText: String 
    var popUpText: String 
    var body: some View { 
        Text(displayedText) 
            .font(.title) 
            .fontWeight(.bold) 
            .onLongPressGesture { 
                showPopover = true 
            } 
            .popover(isPresented: $showPopover) { 
                Text(popUpText) 
                    .frame(width: 250.0) 
                    .onTapGesture { 
                        showPopover = false 
                    } 
            } 
    } 
}

然后修改TopView,移除共享的showPopover状态,直接调用组件即可:

struct TopView: View { 
    @EnvironmentObject var oracleViewModel: OracleViewModel 
    // 删掉原来的共享showPopover变量
    ***Blarg Blarg Blarg*** 
    VStack{ 
        VStack { 
            Text("RUNE") 
                .font(.system(size:10)) 
                .padding(.bottom, 5.0) 
            TextPopUpView(
                displayedText: oracleViewModel.rune, 
                popUpText: oracleViewModel.getDescriptionForRune()
            )
            .environmentObject(oracleViewModel) 
        } 
        .padding(.bottom) 
        VStack { 
            Text("ELEMENT") 
                .font(.system(size:10)) 
                .padding(.bottom, 5.0) 
            TextPopUpView(
                displayedText: oracleViewModel.element, 
                popUpText: oracleViewModel.getDescriptionForElement()
            )
            .environmentObject(oracleViewModel) 
        } 
    } 
    ***Blarg Blarg Blarg*** 
}

这种方案的优势是组件完全自治,后续添加更多弹窗组件时不需要额外维护状态变量,复用性更强。


方案二:为每个组件分配独立的外部状态(适合组件数量少的场景)

如果不想修改TextPopUpView的结构,可以在TopView中为每个弹窗单独声明状态变量:

struct TopView: View { 
    @EnvironmentObject var oracleViewModel: OracleViewModel 
    // 为每个弹窗单独创建状态变量
    @State private var showRunePopover = false
    @State private var showElementPopover = false
    ***Blarg Blarg Blarg*** 
    VStack{ 
        VStack { 
            Text("RUNE") 
                .font(.system(size:10)) 
                .padding(.bottom, 5.0) 
            TextPopUpView(
                showPopover: $showRunePopover, 
                displayedText: oracleViewModel.rune, 
                popUpText: oracleViewModel.getDescriptionForRune()
            )
            .environmentObject(oracleViewModel) 
        } 
        .padding(.bottom) 
        VStack { 
            Text("ELEMENT") 
                .font(.system(size:10)) 
                .padding(.bottom, 5.0) 
            TextPopUpView(
                showPopover: $showElementPopover, 
                displayedText: oracleViewModel.element, 
                popUpText: oracleViewModel.getDescriptionForElement()
            )
            .environmentObject(oracleViewModel) 
        } 
    } 
    ***Blarg Blarg Blarg*** 
}

这种方式更直接,但如果后续需要添加更多弹窗,就得不断新增状态变量,扩展性不如方案一。


额外提示:如果需要全局控制弹窗(比如同时只显示一个)

如果希望确保同一时间只有一个弹窗显示,可以通过EnvironmentObject或者全局状态来管理当前激活的弹窗标识(比如用枚举区分RUNE/ELEMENT),然后在TextPopUpView中判断是否应该显示自己的popover。不过从你的需求来看,前面两种方案已经完全够用了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 22:03:12