将 tapAction 从 SwiftUI 按钮操作发送到 UIView 函数
Send tapAction from SwiftUI button action to UIView function
我正在尝试找到一种方法来触发一个操作,当在 swiftUI
中点击一个按钮时,该操作将调用我的 UIView
中的一个函数。
Here's my setup:
foo()(UIView)
需要 运行 当 Button(SwiftUI)
被点击时
我的自定义 UIView class 使用 AVFoundation 框架
class SomeView: UIView {
func foo() {}
}
要在 swiftUI 中使用我的 UIView,我必须将它包装在 UIViewRepresentable
struct SomeViewRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> CaptureView {
SomeView()
}
func updateUIView(_ uiView: CaptureView, context: Context) {
}
}
托管我的 UIView() 的 SwiftUI 视图
struct ContentView : View {
var body: some View {
VStack(alignment: .center, spacing: 24) {
SomeViewRepresentable()
.background(Color.gray)
HStack {
Button(action: {
print("SwiftUI: Button tapped")
// Call func in SomeView()
}) {
Text("Tap Here")
}
}
}
}
}
您可以将自定义 UIView
的实例存储在您的可表示结构中(SomeViewRepresentable
此处)并在点击操作时调用其方法:
struct SomeViewRepresentable: UIViewRepresentable {
let someView = SomeView() // add this instance
func makeUIView(context: Context) -> SomeView { // changed your CaptureView to SomeView to make it compile
someView
}
func updateUIView(_ uiView: SomeView, context: Context) {
}
func callFoo() {
someView.foo()
}
}
您的视图主体将如下所示:
let someView = SomeViewRepresentable()
var body: some View {
VStack(alignment: .center, spacing: 24) {
someView
.background(Color.gray)
HStack {
Button(action: {
print("SwiftUI: Button tapped")
// Call func in SomeView()
self.someView.callFoo()
}) {
Text("Tap Here")
}
}
}
}
为了测试它,我在 foo()
方法中添加了打印:
class SomeView: UIView {
func foo() {
print("foo called!")
}
}
现在点击您的按钮将触发 foo()
并显示打印语句。
M Reza 的解决方案适用于简单的情况,但是如果您的父 SwiftUI 视图有状态更改,则每次刷新时,它都会导致您的 UIViewRepresentable 创建新的 UIView 实例,原因如下:let someView = SomeView() // add this instance
。因此 someView.foo()
正在调用您创建的 SomeView
的先前实例的操作,该实例在刷新时已经过时,因此您可能看不到 UIViewRepresentable 的任何更新出现在您的父视图上。
参见:https://medium.com/zendesk-engineering/swiftui-uiview-a-simple-mistake-b794bd8c5678
更好的做法是在调用其函数时避免创建和引用 UIView 的实例。
我对 M Reza 解决方案的适应是通过父视图的状态更改间接调用该函数,这会触发 updateUIView
:
var body: some View {
@State var buttonPressed: Bool = false
VStack(alignment: .center, spacing: 24) {
//pass in the @State variable which triggers actions in updateUIVIew
SomeViewRepresentable(buttonPressed: $buttonPressed)
.background(Color.gray)
HStack {
Button(action: {
buttonPressed = true
}) {
Text("Tap Here")
}
}
}
}
struct SomeViewRepresentable: UIViewRepresentable {
@Binding var buttonPressed: Bool
func makeUIView(context: Context) -> SomeView {
return SomeView()
}
//called every time buttonPressed is updated
func updateUIView(_ uiView: SomeView, context: Context) {
if buttonPressed {
//called on that instance of SomeView that you see in the parent view
uiView.foo()
buttonPressed = false
}
}
}
这是使用桥接的另一种方法 class。
//SwiftUI
struct SomeView: View{
var bridge: BridgeStuff?
var body: some View{
Button("Click Me"){
bridge?.yo()
}
}
}
//UIKit or AppKit (use NS instead of UI)
class BridgeStuff{
var yo:() -> Void = {}
}
class YourViewController: UIViewController{
override func viewDidLoad(){
let bridge = BridgeStuff()
let view = UIHostingController(rootView: SomeView(bridge: bridge))
bridge.yo = { [weak self] in
print("Yo")
self?.howdy()
}
}
func howdy(){
print("Howdy")
}
}
我正在尝试找到一种方法来触发一个操作,当在 swiftUI
中点击一个按钮时,该操作将调用我的 UIView
中的一个函数。
Here's my setup:
foo()(UIView)
需要 运行 当 Button(SwiftUI)
被点击时
我的自定义 UIView class 使用 AVFoundation 框架
class SomeView: UIView {
func foo() {}
}
要在 swiftUI 中使用我的 UIView,我必须将它包装在 UIViewRepresentable
struct SomeViewRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> CaptureView {
SomeView()
}
func updateUIView(_ uiView: CaptureView, context: Context) {
}
}
托管我的 UIView() 的 SwiftUI 视图
struct ContentView : View {
var body: some View {
VStack(alignment: .center, spacing: 24) {
SomeViewRepresentable()
.background(Color.gray)
HStack {
Button(action: {
print("SwiftUI: Button tapped")
// Call func in SomeView()
}) {
Text("Tap Here")
}
}
}
}
}
您可以将自定义 UIView
的实例存储在您的可表示结构中(SomeViewRepresentable
此处)并在点击操作时调用其方法:
struct SomeViewRepresentable: UIViewRepresentable {
let someView = SomeView() // add this instance
func makeUIView(context: Context) -> SomeView { // changed your CaptureView to SomeView to make it compile
someView
}
func updateUIView(_ uiView: SomeView, context: Context) {
}
func callFoo() {
someView.foo()
}
}
您的视图主体将如下所示:
let someView = SomeViewRepresentable()
var body: some View {
VStack(alignment: .center, spacing: 24) {
someView
.background(Color.gray)
HStack {
Button(action: {
print("SwiftUI: Button tapped")
// Call func in SomeView()
self.someView.callFoo()
}) {
Text("Tap Here")
}
}
}
}
为了测试它,我在 foo()
方法中添加了打印:
class SomeView: UIView {
func foo() {
print("foo called!")
}
}
现在点击您的按钮将触发 foo()
并显示打印语句。
M Reza 的解决方案适用于简单的情况,但是如果您的父 SwiftUI 视图有状态更改,则每次刷新时,它都会导致您的 UIViewRepresentable 创建新的 UIView 实例,原因如下:let someView = SomeView() // add this instance
。因此 someView.foo()
正在调用您创建的 SomeView
的先前实例的操作,该实例在刷新时已经过时,因此您可能看不到 UIViewRepresentable 的任何更新出现在您的父视图上。
参见:https://medium.com/zendesk-engineering/swiftui-uiview-a-simple-mistake-b794bd8c5678
更好的做法是在调用其函数时避免创建和引用 UIView 的实例。
我对 M Reza 解决方案的适应是通过父视图的状态更改间接调用该函数,这会触发 updateUIView
:
var body: some View {
@State var buttonPressed: Bool = false
VStack(alignment: .center, spacing: 24) {
//pass in the @State variable which triggers actions in updateUIVIew
SomeViewRepresentable(buttonPressed: $buttonPressed)
.background(Color.gray)
HStack {
Button(action: {
buttonPressed = true
}) {
Text("Tap Here")
}
}
}
}
struct SomeViewRepresentable: UIViewRepresentable {
@Binding var buttonPressed: Bool
func makeUIView(context: Context) -> SomeView {
return SomeView()
}
//called every time buttonPressed is updated
func updateUIView(_ uiView: SomeView, context: Context) {
if buttonPressed {
//called on that instance of SomeView that you see in the parent view
uiView.foo()
buttonPressed = false
}
}
}
这是使用桥接的另一种方法 class。
//SwiftUI
struct SomeView: View{
var bridge: BridgeStuff?
var body: some View{
Button("Click Me"){
bridge?.yo()
}
}
}
//UIKit or AppKit (use NS instead of UI)
class BridgeStuff{
var yo:() -> Void = {}
}
class YourViewController: UIViewController{
override func viewDidLoad(){
let bridge = BridgeStuff()
let view = UIHostingController(rootView: SomeView(bridge: bridge))
bridge.yo = { [weak self] in
print("Yo")
self?.howdy()
}
}
func howdy(){
print("Howdy")
}
}