如何保留初始化器并通过@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
相关产品推荐
相关产品推荐

