2017-12-15 10 views
0

例:SpeechSynthesizerクラスを使用して、テキストを完成させたらUIViewで何かを更新する必要があります。 SpeechSynthesizerクラスはプロトコルAVSpeechSynthesizerDelegateに準拠しているため、発声が完了したときにdidFinish信号を受信します。ここでの考え方は、ViewControllerに委任メソッドが多すぎることと、それに従うプロトコルのリストが長くならないようにすることです。私が見つけた回避策は、ViewControllerをSpeechSynthesizer初期化パラメータとして渡すことでした。この方法で、私はSpeechSynthesizerクラスの内部から更新したいUIViewに接続されたViewControllerにアクセスします。私が気に入らないのは、ViewControllerを使用する必要があるすべての単一のクラスにパラメータとして渡すことは醜いようです。だから私はこれを達成できる方法は他にありますか?カスタムクラスをUIViewで動作させる方法はありますか?ViewControllerを初期化時に(参照として)渡さないでください。

私は質問をする別の方法があるとしますどのように私は機能

private func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) 

それはそれで「と呼ばれる」ていないので、ViewControllerをに戻り何かを作ることができますか?

+2

委任パターンを使用する必要があります。それはあなたのオブジェクトを切り離す適切な方法です。 – Paulw11

+0

また、ViewControllerをSpeechSynthesizer初期化パラメータとして渡す代わりに、ViewController操作を行うクロージャを渡すこともできます。 – Spads

答えて

0

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

これは他の人にも役立つはずです。

乾杯!

関連する問題