有没有办法让自定义类在UIView上进行操作而不需要在初始化时传递ViewController(作为参考)?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有没有办法让自定义类在UIView上进行操作而不需要在初始化时传递ViewController(作为参考)?相关的知识,希望对你有一定的参考价值。
示例:我有一个SpeechSynthesizer类,当它完成一段文本时需要更新我的UIView中的内容。由于SpeechSynthesizer类符合协议AVSpeechSynthesizerDelegate,因此它在发声完成时接收didFinish信号。这里的想法是让ViewController不要有太多的委托方法和一长串协议来遵守。我发现的解决方法是将ViewController作为SpeechSynthesizer初始化参数传入。这样我就可以访问连接到我想要从SpeechSynthesizer类内部更新的UIView的ViewController。我不喜欢它的事情是,将ViewController作为参数传递给需要使用它的每个类,看起来很丑陋。所以我想知道,除此之外,我还能做到这一点。
我想另一种问这个问题的方法是:我该如何制作这个功能
private func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance)
将内容返回给ViewController,因为它没有被“调用”?
我在Quora上添加了回复。在这里复制:
在对我自己的代码进行一些研究和测试之后,这里有两个解决方案。
解决方案1:代表模式:
在ViewController中创建自定义委托协议
protocol ViewControllerDelegate:class {
func getViewLayer() -> CALayer
}
ViewController必须符合这个新创建的协议,因此实现了它定义的所有函数,所以你在ViewController类中的某处添加:
public func getViewLayer() -> CALayer {
return self.view.layer
}
然后在我的自定义类ReadTextMachine上,我添加了一个ViewControllerDelegate类型的变量
private weak var viewControllerDelegate: ViewControllerDelegate?
变量必须是弱的,协议必须是类型类才能解决“保留周期”问题(因为自定义类和ViewController都会指向彼此)
你现在会注意到ViewController中的函数调用已经从自定义类中“调用”了,所以在我的ReadTextMachine中我添加了:
let viewLayer = self.viewControllerDelegate?.getViewLayer()
self.cameraPreview = CameraPreview(session: self.camera.getSession(), container: viewLayer!)
self.cameraPreview?.addPreview()
在上面的例子中,我的CameraPreview(是的,本例中的第3类)只是在UIView上添加了一个摄像头预览图层。为此,它需要访问主View的图层。
上面的代码仍然不起作用,因为我们的原始viewController的实例尚未在我们的代码中的任何位置作为引用传递。为此,我们在ReadTextMachine中添加以下函数:
public func setViewControllerDelegate(viewController: ViewController) { // call this from the ViewController so that ViewController can be accessed from here.
self.viewControllerDelegate = viewController
}
在我们实例化自定义类(ReadTextMachine)之后,必须从ViewController调用上面的代码,以便它内部的viewControllerDelegate指向ViewController。所以在我们的ViewController.swift中:
operatingMode = ReadTextMachine()
operatingMode.setViewControllerDelegate(viewController: self)
另一个例子和解释可以在LetsBuildThatApp的video中找到。我主要从它那里得到了我的解决方案
我现在应用上述解决方案的应用程序可以在这里找到:agu3rra/World-Aloud
解决方案2:通知和观察者模式
这个更容易理解和遵循。一般的想法是让您的自定义类广播一条消息,该消息触发ViewController上的函数调用,因为它具有观察者设置,等待听到该消息。
举一个例子,在我使用它的上下文中,我有一个CameraCapture类,它使用AVFoundation来捕获照片。捕获照片触发器无法立即返回图像,因为ios在实际生成图像之前有一组要执行的步骤。我希望我的ReadTextMachine在CameraCapture有照片后恢复活动。 (要在CustomClass触发器的上下文中应用它,ViewController事件基本相同,因为两者都是iOS应用程序中的实际类)。
所以我做的第一件事是创建一个广播功能,因为我会在我的应用程序的许多地方使用它。我只是将它放在Xcode项目的Utilities.swift文件中。
public func broadcastNotification(name: String) {
let notification = Notification.Name(rawValue: name)
NotificationCenter.default.post(name: notification, object: nil)
}
上面的函数接受一个字符串,该字符串必须是唯一的通知标识符,并通过NotificationCenter广播它。
在我的CameraCapture类中,我添加了一个静态常量来引用消息的唯一标识符:
static let NOTIFY_PHOTO_CAPTURED = "agu3rra.worldAloud.photo.captured"
对于那些了解AVFoundation的人来说,当事件didFinishProcessingPhoto被执行时,照片可用,所以在最后我添加了:
broadcastNotification(name: CameraCapture.NOTIFY_PHOTO_CAPTURED)
以上是对我之前定义的效用函数的调用。
为了使我的ReadTextMachine类能够捕获该通知,我在其init()和deinit例程中添加了以下内容:
override init() {
super.init()
// Setup event observers
let notification1 = Notification.Name(rawValue: CameraCapture.NOTIFY_PHOTO_CAPTURED)
NotificationCenter.default.addObserver(self,
selector: #selector(self.processingDoneTakingPhoto),
name: notification1,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self) // cleanup observer once instance no longer exists
}
删除观察者在deinit中非常重要,这样当你的对象从内存中释放出来时,观察者就不会留下徘徊。上面配置的观察者在ReadTextMachine中触发一个函数调用:
@IBAction private func processingDoneTakingPhoto() {
// does my stuff
}
而已!同样,整个Xcode项目可以从我项目的Git存储库下载:agu3rra/World-Aloud
希望这对其他人有用。
干杯!
以上是关于有没有办法让自定义类在UIView上进行操作而不需要在初始化时传递ViewController(作为参考)?的主要内容,如果未能解决你的问题,请参考以下文章
如何让自定义 UIControl 作为推送操作参与 segue?