web-dev-qa-db-ja.com

タイプを持ち、プロトコルを実装する変数をどのように宣言しますか?

私のアプリには、詳細ビューコントローラー用のプロトコルがあり、viewModelプロパティが必要であると述べています。

protocol DetailViewController: class {
    var viewModel: ViewModel? {get set}
}

また、プロトコルを実装するいくつかの異なるクラスがあります。

class FormViewController: UITableViewController, DetailViewController {
    // ...
}

class MapViewController: UIViewController, DetailViewController {
    // ...
}

私のマスタービューコントローラーには、UIViewControllerプロトコルを実装する任意のDetailViewControllerサブクラスに設定できるプロパティが必要です。

残念ながら、これを行う方法に関するドキュメントは見つかりません。 Objective-Cでは、それは簡単です。

@property (strong, nonatomic) UIViewController<DetailViewController>;

Swiftこれを行う構文はありません。最も近いのは、クラス定義でジェネリックを宣言することです。

class MasterViewController<T where T:UIViewController, T:DetailViewController>: UITableViewController {
    var detailViewController: T?
    // ...
}

しかし、「クラス 'MasterViewController'はスーパークラスの必要なメンバーを実装していません」というエラーが表示されます

SwiftはObjective-Cと同じように簡単に実行できるはずですが、どうすればよいかを示唆するものはどこにも見つかりません。

42
Frank Schmitt

Swift 4以降、これを実行できるようになりました。

Swift 4の実装 SE-0156 (クラスとサブタイプの存在)。

このObjective-C構文に相当するもの:

@property (strong, nonatomic) UIViewController<DetailViewController> * detailViewController;

Swift 4では次のようになります。

var detailViewController: UIViewController & DetailViewController

基本的に、変数が準拠する1つのクラスと、変数が実装するN個のプロトコルを定義できます。詳細については、リンク先のドキュメントを参照してください。

10
Senseful

(空の)拡張をUIViewControllerに追加し、空の拡張とdetailViewControllerの合成プロトコルを使用してDetailViewController属性を指定することで、そこに到達できると思います。このような:

protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}

これで、UIViewControllerのすべてのサブクラスがプロトコルUIViewControllerInjectを満たします。次に、単に:

typealias DetailViewControllerComposed = protocol<DetailViewController, UIViewControllerInject>

class MasterViewController : UITableViewController {
  var detailViewController : DetailViewControllerComposed?
  // ...
}

しかし、これは特に「自然」ではありません。

===編集、追加===

実際、提案されたDetailViewControllerを使用してUIViewControllerInjectを定義すると、少し良くなる可能性があります。そのような:

protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}

protocol DetailViewController : UIViewControllerInject { /* ... */ }

これで、明示的に何かを作成する必要はなく(my DetailViewControllerComposed)、detailViewControllerのタイプとしてDetailViewController?を使用できます。

13
GoZoner

別の方法は、プロトコルを実装する適切なUIKitビューコントローラに中間ベースクラスを導入することです。

class MyUIViewControler : UIViewController, DetailViewController ...
class MyUITableViewController : UITableViewController, DetailViewController ...

次に、UIKitではなく、これらのView ControllerからView Controllerを継承します。

これも自然なことではありませんが、GoZonerが提案したように、すべてのUIViewControllersUIViewControllerInjectプロトコルを満たすことを強制するわけではありません。

2
tng