Quoraに返信を追加しました。ここにコピー:
私のコードでいくつかの研究とテストを行った後で、この問題に対する2つの解決法があります。
解決方法1:デリゲートパターン:
のViewControllerは、この新しく作成されたプロトコルに準拠し、したがって、それによって定義されたすべての機能を実装しなければならないのViewController
に
protocol ViewControllerDelegate:class {
func getViewLayer() -> CALayer
}
をカスタムデリゲートプロトコルを作成します。ので、どこかのクラスでは、追加のViewController:
public func getViewLayer() -> CALayer {
return self.view.layer
}
nは私のカスタムクラス、ReadTextMachineに、IはViewControllerDelegate型
private weak var viewControllerDelegate: ViewControllerDelegate?
変数が弱くなければならず、プロトコルは(「サイクルを維持する」という問題を解決するカスタム両方のでするために型クラスでなければならないの変数を追加しましたあなたは私のReadTextMachineに、私は追加してのViewController内部関数呼び出しが、カスタムクラスからすでに「呼び出し可能」であることを今に気づくでしょう
クラスとのViewControllerはお互いを指します):
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
}
をその内部viewControllerDelegateを指すようにコードの上の部分は、私たちは私たちのカスタムクラス(ReadTextMachine)をインスタンス化した後、のViewControllerから呼び出される必要がありますViewController。だから我々のViewControllerで。SWIFT:
operatingMode = ReadTextMachine()
operatingMode.setViewControllerDelegate(viewController: self)
もう一つの例と説明がLetsBuildThatAppからこのvideoで見つけることができます。私は主にそれから私の解決策を導き出しました。上記の溶液を塗布し、開発中
私の現在のアプリはここで見つけることができます:agu3rra/World-Aloud
解決方法2:通知とオブザーバーパターン
この1つは理解し、従うことが少し簡単です。一般的な考え方は、オブザーバの設定があり、そのメッセージを聞くのを待っているので、ViewControllerで関数呼び出しをトリガするメッセージをカスタムクラスにブロードキャストさせることです。
例を挙げれば、私が使った文脈では、AVCoundを使って写真を撮るCameraCaptureクラスがあります。 iOSは実際に画像を生成する前に実行する一連のステップを備えているため、キャプチャフォトトリガはすぐに画像を返すことはできません。 CameraCaptureで写真を利用できるようにした後、ReadTextMachineがアクティビティを再開したかったのです。 (これを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クラスで
、私はメッセージのユニークな識別子参照するために、静的定数を追加しました:最後に、イベントdidFinishProcessingPhotoが実行されます時に写真が利用可能である、AVFoundationを知っている人のために
static let NOTIFY_PHOTO_CAPTURED = "agu3rra.worldAloud.photo.captured"
をので、その私の追加:
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
これは他の人にも役立つはずです。
乾杯!
委任パターンを使用する必要があります。それはあなたのオブジェクトを切り離す適切な方法です。 – Paulw11
また、ViewControllerをSpeechSynthesizer初期化パラメータとして渡す代わりに、ViewController操作を行うクロージャを渡すこともできます。 – Spads