英文:
Why does the `Binding<Bool>` in XUIView does not see the updated value?
问题
The code you provided is in Swift and contains technical details related to SwiftUI and UIKit integration. Here's a translation of the code comments and some key parts:
为什么在`XUIView`中的`Binding<Bool>`看不到更新后的值?
我有一个`SwiftUI`的`XView`,它包装了一个`XUIView`。在`XUIView`中,我有一个`Binding<Bool>`,它是使用`XView`的`@Binding...`初始化的。在`XUIView`中,我有一个`UIButton`,我可以使用`XUIView`中的“Binding”属性来切换`customFlag`。这在`SwiftUI`视图中很好地传播,因为`Text`正确更新。但是,`UIButton`的标题没有正确更新。`XUIView._update`方法无法“看到”新值。
我在这里漏掉了什么?
我是否需要在`updateUIView`方法中分配`customFlag`以使其工作?
带有一些打印调用以查看发生了什么的示例代码:
// 这里是示例代码,包括一些打印调用以查看发生的情况。
Please note that the translation retains the technical terms and code structure, but the code itself is unchanged. If you have any specific questions about the code or need further assistance, please let me know.
英文:
Why does the Binding<Bool>
in XUIView
does not see the updated value?
I have a SwiftUI
XView
which wraps an XUIView
. In the XUIView
I have a Binding<Bool>
, which is initialised with the @Binding...
of the XView
. In the XUIView
I have a UIButton
with which I toggle the customFlag
using the "Binding" property in XUIView
. That propagates nicely into the SwiftUI
View, as the Text
updates properly. However, the UIButton
's title is not updated properly. The XUIView._update
method does not "see" the new value.
What am I missing here?
Do I have to assign the customFlag
in the updateUIView
method to make that work?
Sample code with some print calls to see what's going on:
import SwiftUI
struct UIKitBindingView: View {
@State private var customFlag: Bool = false
var body: some View {
VStack {
Text("customFlag: \(customFlag ? "ON" : "OFF")")
XView(customFlag: $customFlag)
.frame(width: 200, height: 100)
}
}
}
struct UIKitBindingView_Previews: PreviewProvider {
static var previews: some View {
UIKitBindingView()
}
}
struct XView: UIViewRepresentable {
typealias UIViewType = XUIView
typealias Context = UIViewRepresentableContext<XView>
@Binding var customFlag: Bool
init(customFlag: Binding<Bool>) {
self._customFlag = customFlag
}
func makeUIView(context: Context) -> XUIView {
Swift.print("XView: \(#function)")
let v = XUIView(frame: .zero, customFlag: $customFlag)
return v
}
func updateUIView(_ uiView: XUIView, context: Context) {
Swift.print("XView: \(#function)")
// uiView.customFlag = $customFlag
Swift.print("XView: \(#function): customFlag: \(customFlag) \n--\($customFlag)")
uiView._update()
}
}
class XUIView: UIView {
weak var button: UIButton?
var customFlag: Binding<Bool>
init(frame: CGRect, customFlag: Binding<Bool>) {
self.customFlag = customFlag
super.init(frame: frame)
Swift.print("XUIView: \(#function)")
_configure()
}
required init?(coder: NSCoder) {
self.customFlag = Binding(get: { true }, set: { v, t in })
super.init(coder: coder)
Swift.print("XUIView: \(#function)")
_configure()
}
deinit {
Swift.print("XUIView: \(#function)")
}
func _update() {
Swift.print("XUIView: \(#function): \(customFlag.wrappedValue)\n--\(customFlag)")
button?.setTitle("BUTTON:" + (customFlag.wrappedValue ? "ON" : "OFF"), for: .normal)
}
private func _configure() {
backgroundColor = .yellow
let b = UIButton()
button = b
self.addSubview(b)
b.translatesAutoresizingMaskIntoConstraints = false
let cons = [
self.centerXAnchor.constraint(equalTo: b.centerXAnchor),
self.centerYAnchor.constraint(equalTo: b.centerYAnchor),
]
NSLayoutConstraint.activate(cons)
b.setTitleColor(.systemBlue, for: .normal)
b.addAction(UIAction(handler: { action in
Swift.print("XUIView: \(#function): button action ***")
self.customFlag.wrappedValue.toggle()
}), for: .touchUpInside)
_update()
}
}
Console output after tapping the button:
XUIView: __preview___configure(): button action ***
XView: __preview__updateUIView(_:context:)
XView: __preview__updateUIView(_:context:): customFlag: true
--Binding<Bool>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.Binding<Swift.Bool>.(unknown context at $10594f2e0).ScopedLocation>, _value: true)
XUIView: __preview___update(): false
--Binding<Bool>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.Binding<Swift.Bool>.(unknown context at $10594f2e0).ScopedLocation>, _value: false)
答案1
得分: 1
Binding<Bool>
作为字面量并未记录为 Binding
的已记录用法。
Binding
应该作为 @Binding
属性包装器使用。
https://developer.apple.com/documentation/swiftui/binding
还要注意,Binding
符合 DynamicProperty
,因此它应仅在符合 DynamicProperty
的 View
或 struct
中使用。
在重新计算视图主体之前,视图为这些属性提供值。
您可以使用 updateUIView
从 SwiftUI 通信到 UIKit,从 UIKit 到 SwiftUI 可以使用完成处理程序、共享的真相源、委托等。
英文:
Binding<Bool>
as a literal isn’t a documented used of Binding
.
Binding should be used as an @Binding
property wrapper.
https://developer.apple.com/documentation/swiftui/binding
It is also important to note that Binding conforms to DynamicProperty
therefore it should only be used in a View
or a struct
that also conforms to DynamicProperty
.
> The view gives values to these properties prior to recomputing the view’s body.
You can communicate from SwiftUI to UIKit using updateUIView
and from UIKit to SwiftUI using completion handlers, a shared source of truth, a delegate, etc.
答案2
得分: 0
请参考以下翻译:
将 XUIView 的 _update()
调用(删除该方法)替换为直接在 updateUIView(_ uiView: XUIView, context: Context)
中进行更新:
func updateUIView(_ uiView: XUIView, context: Context) {
uiView.button?.setTitle("BUTTON:" + ($customFlag.wrappedValue ? "ON" : "OFF"), for: .normal)
}
英文:
Instead of calling the _update()
of the XUIView (remove that method), make the update directly in the updateUIView(_ uiView: XUIView, context: Context)
:
func updateUIView(_ uiView: XUIView, context: Context) {
uiView.button?.setTitle("BUTTON:" + ($customFlag.wrappedValue ? "ON" : "OFF"), for: .normal)
}
答案3
得分: 0
你忘记了用新的绑定值更新UIView
,例如:
func updateUIView(_ uiView: XUIView, context: Context) {
...
uiView.customFlag = _customFlag
...
}
请注意,仅仅传递绑定是有点懒惰的做法。最好是设置你自己的闭包。绑定基本上是一对获取和设置闭包,但你的协调器只关心一个设置闭包,所以可以创建一个didChangeCustomFlag
并传递一个设置可表示对象绑定的闭包。请参考Fruta的PaymentButton.swift
以获取此模式的示例。
英文:
You forgot to update the UIView
with the new binding value, e.g.
func updateUIView(_ uiView: XUIView, context: Context) {
...
uiView.customFlag = _customFlag
...
}
Note it is a little bit lazy to just pass the binding along. It would be better to set your own closure. Binding is basically a pair of get and set closures, but your coordinator is only interested in a set closure, so could just create a didChangeCustomFlag and pass a closure that sets the representable's binding. See Fruta's PaymentButton.swift for an example of this pattern.
答案4
得分: 0
我认为问题出在 Binding 类型的不正确使用,XUIView 中的 BindingBool> 无法看到更改后的值。在初始化自定义标志时,需要将 XUIView 更改为 'self.$_customFlag'。
let v = XUIView(frame: .zero, customFlag: self.$_customFlag)
在 updateUIView 函数中,可以删除有关更新绑定变量的注释行。
uiView.customFlag = $_customFlag
英文:
I think the issue is the Binding type was used improperly, the BindingBool> in the XUIView was unable to see the changed value. You need to change the XUIView when initializing the custom flag you need to declare it as 'self.$_customFlag'.
func makeUIView(context: Context) -> XUIView {
Swift.print("XView: \(#function)")
let v = XUIView(frame: .zero, customFlag: self.$customFlag)
return v
}
also in the updateUIView function, you can remove the comment line for updating the binding variable.
uiView.customFlag = $customFlag
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论