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

如何保留初始化器并通过@Binding跨视图修改Bool值?

问题解决:点击子视图按钮更新父视图变量的Binding初始化错误

需求与问题

需要点击子视图(自定义Button)的按钮时,将父视图(RequestRow)中的showingRequest变量设为false。尝试使用@Binding实现双向绑定时,遇到以下初始化报错,且无法删除现有初始化器(其他视图依赖该初始化器):

  • self在所有存储属性初始化前被使用
  • 初始化器未初始化所有存储属性即返回

原代码

RequestRow视图

struct RequestRow: View {
    @EnvironmentObject var model: AppStateModel
    @State var showingRequest = false // this is the variable
    
    let type: MessageType
    let text: String
    let newMessageId: String
    
    var isSender: Bool {
        return type == .sent
    }
    
    init(text: String, type: MessageType, newMessageId: String, showingRequest: Bool) {
        self.text = text
        self.type = type
        self.newMessageId = newMessageId
        self.showingRequest = showingRequest
    }
    
    var body: some View {
            HStack {
                if isSender { Spacer() }
                if !isSender {
                    VStack {
                        Spacer()
                        Image(model.currentUsername == "joe" ? "photo1" : "photo2")
                            .resizable()
                            .scaledToFit()
                            .frame(width: 45, height: 45)
                            .foregroundColor(Color.pink)
                            .clipShape(Circle())
                    }
                }
                VStack{
                    HStack {
                        Text("This user want's to send you this request\n\n\(text)\n\nDo you wanna accept this request?")
                            .foregroundColor(isSender ? Color.white : Color(.white))
                            .padding()
                    }
                    if !isSender && ShowingRequest {
                        Button(requestId: newMessageId, ShowingRequest: false)
                    }
                }
                    .background(isSender ? Color.blue : Color(.black))
                    .cornerRadius(10)
                    .padding(isSender ? .leading : .trailing, isSender ? UIScreen.main.bounds.width/4 : UIScreen.main.bounds.width/6)
                if !isSender { Spacer() }

            }
            .onAppear{
                model.getShowAcceptValue(requestId: newMessageId) { showAccept in
                    print("ShowAccept value: \(showAccept)")
                    showingRequest = showAccept
                }
            }
        }
    }

自定义Button视图

struct Button: View {
    @EnvironmentObject var model: AppStateModel
    @State var showingRequest : Bool
    var requestId: String
    init(requestId: String, showingRequest: Bool) {
        self.requestId = requestId
        self.showingRequest = showingRequest
    }
    var body: some View {
        HStack(spacing: 0) {
            Button(action: {
                self.updateShowAcceptToFalse()
                showingRequest.toggle() // this is the code that updates the value
            }) {
                ZStack {
                    Color.green
                    Text("Accept")
                        .foregroundColor(.white)
                }
            }
            .frame(maxWidth: .infinity, maxHeight: 60) // set the height of the button
            
            Button(action: {
                self.updateShowAcceptToFalse()
                showingRequest.toggle() // this is also the code that updates the value
            }) {
                ZStack {
                    Color.red
                    Text("Deny")
                        .foregroundColor(.white)
                }
            }
            .frame(maxWidth: .infinity, maxHeight: 60) // set the height of the button
        }
        .frame(height: 60) // set the height of the HStack container
    }
    private func updateShowAcceptToFalse() {
        model.updateShowAcceptToFalse(requestId: requestId)
    }
}

解决方案

1. 修改自定义Button视图(重命名为ActionButton避免冲突)

将视图内的@State改为@Binding,实现与父视图的双向同步,同时修正初始化器写法:

struct ActionButton: View {
    @EnvironmentObject var model: AppStateModel
    @Binding var showingRequest: Bool
    var requestId: String
    
    init(requestId: String, showingRequest: Binding<Bool>) {
        self.requestId = requestId
        self._showingRequest = showingRequest
    }
    
    var body: some View {
        HStack(spacing: 0) {
            Button(action: {
                updateShowAcceptToFalse()
                showingRequest = false // 直接设为false,符合需求
            }) {
                ZStack {
                    Color.green
                    Text("Accept")
                        .foregroundColor(.white)
                }
            }
            .frame(maxWidth: .infinity, maxHeight: 60)
            
            Button(action: {
                updateShowAcceptToFalse()
                showingRequest = false
            }) {
                ZStack {
                    Color.red
                    Text("Deny")
                        .foregroundColor(.white)
                }
            }
            .frame(maxWidth: .infinity, maxHeight: 60)
        }
        .frame(height: 60)
    }
    
    private func updateShowAcceptToFalse() {
        model.updateShowAcceptToFalse(requestId: requestId)
    }
}

2. 修改RequestRow视图

保留原有初始化器,正确初始化@State属性,并传递showingRequest的Binding给子视图:

struct RequestRow: View {
    @EnvironmentObject var model: AppStateModel
    @State var showingRequest = false
    
    let type: MessageType
    let text: String
    let newMessageId: String
    
    var isSender: Bool {
        return type == .sent
    }
    
    init(text: String, type: MessageType, newMessageId: String, showingRequest: Bool) {
        self.text = text
        self.type = type
        self.newMessageId = newMessageId
        self._showingRequest = State(initialValue: showingRequest) // 正确初始化@State
    }
    
    var body: some View {
        HStack {
            if isSender { Spacer() }
            if !isSender {
                VStack {
                    Spacer()
                    Image(model.currentUsername == "joe" ? "photo1" : "photo2")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 45, height: 45)
                        .foregroundColor(Color.pink)
                        .clipShape(Circle())
                }
            }
            VStack{
                HStack {
                    Text("This user wants to send you this request\n\n\(text)\n\nDo you wanna accept this request?")
                        .foregroundColor(.white)
                        .padding()
                }
                // 传递Binding并修正变量名大小写
                if !isSender && showingRequest {
                    ActionButton(requestId: newMessageId, showingRequest: $showingRequest)
                }
            }
            .background(isSender ? Color.blue : Color.black)
            .cornerRadius(10)
            .padding(isSender ? .leading : .trailing, isSender ? UIScreen.main.bounds.width/4 : UIScreen.main.bounds.width/6)
            if !isSender { Spacer() }
        }
        .onAppear{
            model.getShowAcceptValue(requestId: newMessageId) { showAccept in
                print("ShowAccept value: \(showAccept)")
                showingRequest = showAccept
            }
        }
    }
}

关键修改说明

  • 避免组件重名:将自定义Button重命名为ActionButton,防止与系统Button冲突
  • Binding正确使用:子视图用@Binding接收父视图状态,父视图通过$符号传递State的Binding
  • @State初始化修正:在RequestRow的初始化器中,通过_showingRequest = State(initialValue: showingRequest)正确初始化@State属性,解决初始化报错
  • 变量名大小写统一:修正原代码中ShowingRequest的大小写错误,确保与定义一致
  • 明确赋值逻辑:直接将showingRequest设为false,替代toggle操作,符合需求逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 05:48:09