如何通过 SwiftUI 按钮在 UIViewController 中执行功能?
Posted
技术标签:
【中文标题】如何通过 SwiftUI 按钮在 UIViewController 中执行功能?【英文标题】:How to execute a function in a UIViewController through a SwiftUI Button? 【发布时间】:2019-12-02 16:32:24 【问题描述】:在下面的代码中,我试图弄清楚如何使用SwiftUI
按钮执行savePhoto
函数。这实际上是我试图解决的问题的简化版本,但仍然无法弄清楚。我在ContentView
中创建的按钮是我想象的解决问题的方法,但由于 SwiftUI 结构,我似乎找不到解决方法。
这是我的 SwiftUI 视图:
struct ContentView: View
var body: some View
ZStack
AnotherControllerView()
Button(action: AnotherControllerView.savePhoto() )
Text("Save Photo")
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
还有我的集成 UIViewController:
import Foundation
import SwiftUI
import UIKit
struct AnotherControllerView : UIViewControllerRepresentable
typealias UIViewControllerType = AnotherController
func makeCoordinator() -> AnotherControllerView.Coordinator
Coordinator(self)
func makeUIViewController(context: UIViewControllerRepresentableContext<AnotherControllerView>) -> AnotherController
return AnotherController()
func updateUIViewController(_ uiViewController: AnotherController, context: UIViewControllerRepresentableContext<AnotherControllerView>)
class Coordinator : NSObject
var parent : AnotherControllerView
init(_ viewController : AnotherControllerView)
self.parent = viewController
class AnotherController : UIViewController
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = UIColor.blue
func savePhoto()
let alert = UIAlertController(title: "Save Photo to Camera Roll", message: "Would you like to save your drawing to the camera roll?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: someHandler))
self.present(alert, animated: true)
func someHandler(alert: UIAlertAction!)
print("Handler executed")
【问题讨论】:
【参考方案1】:你好要控制 UIViewController 我们需要在 View 和 UIViewControllerRepresentable 之间创建一个绑定
让我们用代码解释一下:
首先,您需要在AnotherControllerView
中声明一个带有@Binding
注释的新变量
会是这样的:
struct AnotherControllerView : UIViewControllerRepresentable
@Binding var isShown: Bool
typealias UIViewControllerType = AnotherController
func makeCoordinator() -> AnotherControllerView.Coordinator
Coordinator()
func makeUIViewController(context: UIViewControllerRepresentableContext<AnotherControllerView>) -> AnotherController
return AnotherController(isShown: $isShown)
func updateUIViewController(_ controller: AnotherController, context: UIViewControllerRepresentableContext<AnotherControllerView>)
if(self.isShown)
controller.savePhoto()
class Coordinator : NSObject
为此,我们在updateUIViewController
中实现逻辑
class AnotherController : UIViewController
@Binding var isShown: Bool
init(isShown: Binding<Bool>)
_isShown = isShown
super.init(nibName: nil, bundle: nil)
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = UIColor.blue
func savePhoto()
let alert = UIAlertController(title: "Save Photo to Camera Roll", message: "Would you like to save your drawing to the camera roll?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: cancelAlert))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: someHandler))
self.present(alert, animated: true)
func cancelAlert(alert: UIAlertAction!)
self.isShown = false
func someHandler(alert: UIAlertAction!)
print("Handler executed")
如果你想捕捉图像,让我告诉你我是如何实现的:
struct CaptureImageView
@Binding var isShown: Bool
@Binding var image: Image?
var sourceType: UIImagePickerController.SourceType
func makeCoordinator() -> Coordinator
return Coordinator(isShown: $isShown, image: $image)
extension CaptureImageView: UIViewControllerRepresentable
func makeUIViewController(context: UIViewControllerRepresentableContext<CaptureImageView>) -> UIImagePickerController
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.sourceType = sourceType
return picker
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<CaptureImageView>)
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate
@Binding var isCoordindatorShown: Bool
@Binding var imageInCoordinator: Image?
init(isShown: Binding<Bool>, image: Binding<Image?>)
_isCoordindatorShown = isShown
_imageInCoordinator = image
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
guard let unwrapImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else return
imageInCoordinator = Image(uiImage: unwrapImage)
isCoordindatorShown = false
func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
isCoordindatorShown = false
我只是在正文中调用:
CaptureImageView(isShown: $showCaptureImageView, image: $imageCaptured, sourceType: importSourceType)
【讨论】:
UIViewController 的指定初始化器是 initWithNibName:bundle:。您应该改为调用它。如果您没有 nib,请为 nibName 传入 nil(bundle 也是可选的)。然后你可以在 loadView 中构建自定义视图,或者在 viewDidLoad 中为 self.view 添加子视图,就像你以前一样。 非常感谢。我做了 twerk 并且能够实现我所追求的,这只是在 UIViewController 中保存一个 UIImage。但是,在执行savePhoto()
时,您没有收到双重警报吗?似乎它希望您通过 SwiftUI 运行警报。 2019-12-02 12:54:27.434741-0500 CoordinatorVC[1048:209323] Warning: Attempt to present <UIAlertController: 0x101888400> on <CoordinatorVC.AnotherController: 0x100c13010> which is already presenting (null)
是的,你是对的,我得到了两次,我添加了一个警报,正如你直接在视图上所说的
那么你检查是否设置了一个布尔值,当它设置时,调用相应的函数?当通过不同的状态更新再次调用updateUIViewController
时会发生什么?除非您在函数末尾重置布尔值,否则将错误地再次调用该函数。 然而如果你在函数结束时重置布尔值,那么你就会陷入状态修改循环:“在视图更新期间修改状态,这将导致未定义的行为。”【参考方案2】:
如果 AnotherControllerView 很大并且您很少在新版本中使用它,您可以考虑使用更简单的解决方法。
struct ContentAnotherControllerView: View
let anotherControllerView = AnotherControllerView()
var body: some View
ZStack
anotherControllerView
Button(action: self.anotherControllerView.savePhoto() )
Text("Save Photo")
struct AnotherControllerView : UIViewControllerRepresentable
typealias UIViewControllerType = AnotherController
var savePhotoDelegate: AnotherController? = AnotherController()
func savePhoto()
savePhotoDelegate?.savePhoto()
func makeUIViewController(context: UIViewControllerRepresentableContext<AnotherControllerView>) -> AnotherController
if let savePhotoDelegate = savePhotoDelegate
return savePhotoDelegate
return AnotherController()
....
这种方法只是提醒我们一个视图可以在某些情况下重用。由于 UIView 桥不是纯 SwiftUI 域,你可以试试这种方式。
【讨论】:
以上是关于如何通过 SwiftUI 按钮在 UIViewController 中执行功能?的主要内容,如果未能解决你的问题,请参考以下文章
当我们在文本和图像之间切换按钮内容时,如何停止 SwiftUI 更改固定填充?