如何绑定 SwiftUI 和 UIViewController 行为
Posted
技术标签:
【中文标题】如何绑定 SwiftUI 和 UIViewController 行为【英文标题】:How to bind SwiftUI and UIViewController behavior 【发布时间】:2021-08-05 16:40:17 【问题描述】:我有一个带有 UIViewControllers 的 UIKit 项目,我想通过我的 ViewController 展示一个基于 SwiftUI 构建的操作表。我需要将操作表的外观和消失绑定回视图控制器,使视图控制器能够被关闭(并且显示动画仅在 viewDidAppear 上发生,以避免在使用 .onAppear
时发生一些奇怪的动画行为)。这是一个代码示例,说明了我希望绑定如何工作以及它如何没有按照我的预期进行:
import UIKit
import SwiftUI
class ViewController: UIViewController
let button = UIButton(type: .system)
var show = true
lazy var isShowing: Binding<Bool> = .init
self.show
set: show in
// This code gets called
self.show = show
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .white
button.setTitle("TAP THIS BUTTON", for: .normal)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.addTarget(self, action: #selector(tapped), for: .touchUpInside)
@objc private func tapped()
let vc = UIHostingController(rootView: BindingProblemView(testBinding: isShowing))
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: false)
DispatchQueue.main.asyncAfter(deadline: .now() + 5) [self] in
isShowing.wrappedValue.toggle()
isShowing.update()
struct BindingProblemView: View
@Binding var testBinding: Bool
@State var state = "ON"
var body: some View
ZStack
if testBinding
Color.red.ignoresSafeArea().padding(0)
else
Color.green.ignoresSafeArea().padding(0)
Button("Test Binding is \(state)")
testBinding.toggle()
.onChange(of: testBinding, perform: value in
// This code never gets called
state = testBinding ? "ON" : "OFF"
)
当我设置绑定值true
时,onChange
永远不会在viewDidAppear
之后被调用。我只是在完全滥用新的组合运算符吗?
【问题讨论】:
我无法让示例在屏幕上显示任何内容,这似乎是CustomActionSheet
的问题,您能否提供一个要点/repo 以便我们直接尝试?
@George 这是指向要点的链接gist.github.com/vargero/7a018471856be3be06ef45910f6203df 我已经删除了圆角,因为这是自定义代码,但代码现在应该可以工作了。因为它没有显示任何内容,只有当您在 .onAppear
上调用 showActionSheet
时
即使在一个完全空白的 UIKit 项目中,屏幕上也没有任何显示(代码也来自 gist)
@George 我用所有需要的类更新了 Gist。当有一个以 ViewController
开头的新 Storyboard 项目时,此方法有效
【参考方案1】:
您可以通过ObservableObject
s 传递数据,而不是通过Binding
s。这里的想法是ViewController
具有对PassedData
实例的引用,该实例被传递给SwiftUI 视图,该视图接收对对象的更改,因为它是@ObservedObject
。
现在可以使用了,因此您可以单击原始按钮来呈现 SwiftUI 视图。然后,该视图中的按钮切换passedData.isShowing
,这会改变背景颜色。由于这是一个类实例,ViewController
也可以访问该值。例如,isShowing
也在 5 秒后在tapped()
内切换,以显示该值可以从ViewController
更改或 BindingProblemView
。
虽然不再需要,但onChange(of:perform:)
仍会触发。
代码:
class PassedData: ObservableObject
@Published var isShowing = true
class ViewController: UIViewController
let button = UIButton(type: .system)
let passedData = PassedData()
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .white
button.setTitle("TAP THIS BUTTON", for: .normal)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.addTarget(self, action: #selector(tapped), for: .touchUpInside)
@objc private func tapped()
let newView = BindingProblemView(passedData: passedData)
let vc = UIHostingController(rootView: newView)
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: false)
// Example of toggling from in view controller
DispatchQueue.main.asyncAfter(deadline: .now() + 5)
self.passedData.isShowing.toggle()
struct BindingProblemView: View
@ObservedObject var passedData: PassedData
var body: some View
ZStack
if passedData.isShowing
Color.red.ignoresSafeArea().padding(0)
else
Color.green.ignoresSafeArea().padding(0)
Button("Test Binding is \(passedData.isShowing ? "ON" : "OFF")")
passedData.isShowing.toggle()
结果:
【讨论】:
以上是关于如何绑定 SwiftUI 和 UIViewController 行为的主要内容,如果未能解决你的问题,请参考以下文章