2017-03-13 3 views
9

UITableViewCellサブクラスでの「設定」メソッドの呼び出しを簡略化したいと思います。ただし、すべてのセットアップ方法が同じではありませんが、パラメータは同じ種類から継承されます。ジェネリックやプロトコルで毎回パラメータをキャストする必要はありませんか?UITableViewCellへのサブクラスのメソッドを、プロトコルまたは基本クラスに基づく汎用パラメータ値を使用して呼び出します。

まずはこのようなcellForRow-方法:

class DataSource<V : UIViewController, T: TableViewCellData, VM: ViewModel> : NSObject, UITableViewDataSource, UITableViewDelegate { 

    var dataCollection: TableViewDataCollection<T>! 
    var viewModel: VM! 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cellData = dataCollection.object(for: indexPath) 
     let cell = tableView.dequeueReusableCell(withIdentifier: cellData.identifier(), for: indexPath) 
     if let setupableCell = cell as? CellDataSetupable { 
      setupableCell.setup(with: cellData, viewModel: viewModel) 
     } 
     return cell 
    } 
} 

protocol CellDataSetupable : class { 
    func setup(with cellData: TableViewCellData, viewModel: ViewModel) 
} 

どこcellDataとのViewModelで私のセットアップの細胞を。私の(多くの)カスタムUITableViewCellのサブクラスで

HomeViewTableViewCellDataTableViewCellDataHomeViewModelのサブクラスである

extension BlurbTableViewCell : CellDataSetupable { 
    func setup(with cellData: TableViewCellData, viewModel: ViewModel) { 
     guard let cellData = cellData as? HomeViewTableViewCellData else { return } 
     guard let viewModel = viewModel as? HomeViewModel else { return } 

     // Use cellData and viewModel to setup cell appearance 
    } 
} 

ViewModel

のサブクラスである代わりに、私はガードを削除し、直接このような何かを書きたいです:

extension BlurbTableViewCell : CellDataSetupable< { 
    func setup(with cellData: HomeViewTableViewCellData, viewModel: HomeViewModel) { 
     // Use cellData and viewModel to setup cell appearance 
    } 
} 

olutions(それが動作しません):

  • これはcellForRow方法で鋳造にエラーが発生ししかし、私は、「プロトコルのプロトコル上のassociatedtype/typealiasを使用することはできません...一般的な制約としてのみを使用することができますそれは自己またはそれに関連するタイプ要件があるからです。 "任意のアイデア

(も編集下記2を参照)How to create generic protocols in Swift?または私は私と一緒に暮らす必要があります:インターフェイスビルダーは、その後クラッシュするように私はまた、一般的な基底クラスを使用することはできません"Unknown class in interface builder file" when using class which inherits from a generic class in a Storyboard

  • :私はこの方法を試してみました鋳物?

    編集1:

    // This works fine 
    extension TransactionTableViewCell : CellDataSetupable { 
        typealias CellData = HomeViewTableViewCellData 
        typealias VM = HomeViewModel 
        func setup(with cellData: CellData, viewModel: VM) { 
         //Setup cell appearance ... 
        } 
    } 
    

    この行はまた、正常に動作します::私は(私はいくつかの一般的な種類の名前を変更したことに注意してください)このコードを試してみました下のネイト・マンからの提案の後 (余分なwhere句に注意してください)

    class DataSource<VC : UIViewController, TVCD: TableViewCellData, VM: ViewModel, CDS: CellDataSetupable> : NSObject, UITableViewDataSource, UITableViewDelegate where CDS.TVCD == TVCD, CDS.VM == VM { 
        // ... 
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
         let cellData = dataCollection.object(for: indexPath) 
         let cell = tableView.dequeueReusableCell(withIdentifier: cellData.identifier(), for: indexPath) 
         if let setupableCell = cell as? CDS { 
          setupableCell.setup(with: cellData, viewModel: viewModel) 
         } 
         return cell 
        } 
    

    しかし

    class HomeTableViewDataSource: DataSource<HomeViewController, HomeViewTableViewCellData, HomeViewModel> { 
    
    から変更

    Using 'CellDataSetupable' as a concrete type confirming to protocol 'CellDataSetupable' is not supported 
    

    編集2:

    このエラーを与える

    class HomeTableViewDataSource: DataSource<HomeViewController, HomeViewTableViewCellData, HomeViewModel, CellDataSetupable> { 
    
    から
    のUITableViewCellクラスのジェネリック基底クラスの具体的なバージョンを使用すると、いずれかのことはできません。 Why can't interface builder use a concrete generic subclass of of UIView?

  • 答えて

    0

    私自身の答えは現時点では不可能だということです。

    ネイトマンの答えは私が望まない具体的な実装をCellDataSetupableと指定する必要があります。

    timaktimakの回答には、具体的な実装が必要か、UIViewのサブクラス化が必要です。

    1

    私は、これは動作するはずだと思う:あなたはDataSourceを使用している場合は

    class DataSource<V : UIViewController, T: TableViewCellData, VM: ViewModel, X: CellDataSetupable> : NSObject where X.M == VM, X.T == T { 
    
        var dataCollection: TableViewDataCollection! 
        var viewModel: VM! 
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
         let cellData = dataCollection.object(for: indexPath) as! T 
         let cell = tableView.dequeueReusableCell(withIdentifier: cellData.identifier(), for: indexPath) 
         if let setupableCell = cell as? X { 
          setupableCell.setup(with: cellData, viewModel: viewModel) 
         } 
         return cell 
        } 
    } 
    
    
    protocol CellDataSetupable: class { 
        associatedtype T: TableViewCellData 
        associatedtype M: ViewModel 
        func setup(with cellData: T, viewModel: M) 
    } 
    
    
    class BlurbTableViewCell: UITableViewCell, CellDataSetupable { 
        func setup(with cellData: HomeViewTableViewCellData, viewModel: HomeViewModel) { 
    
         // Use cellData and viewModel to setup cell appearance 
        } 
    } 
    
    +0

    お願いします。面白い。 – hasan83

    +0

    私はcellwilldisplay tableviewデリゲートメソッドでセルパラメタの型を変更するだけで同じことをしようとしています。それは動作しません。このメソッドはオーバーライドとはみなされません。多分彼らはこのようにそれを実装しませんでした。 – hasan83

    +0

    私を助けてくれてありがとう。これは正しい方向への試みかもしれません。しかし、それはDataSourceの私のサブクラスで不平を言う。 CellDataSetupableを汎用パラメータとして追加したので、これにいくつか入力する必要がありますが、 'CellDataSetupableプロトコルに準拠した具体的な型としてCellDataSetupableを使用することはサポートされていません。 'が生成されます。より多くのコードについては上記の「編集1」を参照してください。 – Sunkas

    0

    、あなたはDataSourceで扱う具体的な種類を提供します。だから、

    class HomeTableViewDataSource: DataSource<HomeViewController, HomeViewTableViewCellData, HomeViewModel, CellDataSetupable> { 
    

    は本当に

    HomeTableViewCellは( BlurbTableViewCellまたは TransactionTableViewCellのような) CellDataSetupableに準拠して、具体的な UITableViewCellサブクラスである
    class HomeTableViewDataSource: DataSource<HomeViewController, HomeViewTableViewCellData, HomeViewModel, HomeTableViewCell> { 
    

    でなければなりません。それはうまくいくでしょう。

    ここには、動作していておそらく理解しやすい単純な汎用TableViewDataSourceの例があります。

    ジェネリックタイプ:上記のコードの上に構築されている

    import UIKit 
    
    protocol CellDataProtocol { 
        var cellIdentifier: String { get } 
    } 
    
    protocol ConfigurableCell { 
        associatedtype Data: CellDataProtocol 
        func configure(with data: Data) 
    } 
    
    class DataSource<CellData: CellDataProtocol, Cell: ConfigurableCell>: NSObject, UITableViewDataSource where Cell.Data == CellData { 
    
        var data: [CellData]! 
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
         return data.count 
        } 
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
         let cellData = data[indexPath.row] 
         let cell = tableView.dequeueReusableCell(withIdentifier: cellData.cellIdentifier, for: indexPath) 
         if let cell = cell as? Cell { 
          cell.configure(with: cellData) 
         } 
         return cell 
        } 
    } 
    

    コンクリートクラス:

    class DataObject: CellDataProtocol { 
        var cellIdentifier: String { 
         return "Identifier" 
        } 
    
        // an additional property of the class 
        var number: Int { 
         return 5 
        } 
    } 
    
    class TableViewCell: UITableViewCell, ConfigurableCell { 
        func configure(with data: DataObject) { 
         // configure cell UI with data 
    
         // properties can be used here 
         // since the exact type is known 
         self.textLabel?.text = "\(data.number)" 
        } 
    } 
    
    let dataSource = DataSource<DataObject, TableViewCell>() 
    
    class ConcreteDataSource: DataSource<DataObject, TableViewCell> { 
        // additional functionality 
    } 
    

    編集:あなたが望む場合、サブクラスはまた、異なる種類の細胞を扱うことができます。

    class SubDataSource<Cell: ConfigurableCell>: DataSource<DataObject, Cell> where Cell.Data == DataObject { 
        // additional functionality 
    } 
    
    let d = SubDataSource<TableViewCell>() 
    

    これが役に立ちます。がんばろう!

    +0

    あなたの答えをありがとう。しかし、DataSourceが異なる型を持つことができるように 'HomeTableViewCell'を具体的に追加することはできません。 'UITableView'には、' UITableViewCell'のセルとサブクラスのタイプが異なります。私は自分自身をこれらのタイプに限定したくない。私が持っていた問題は、これらのセルにプロトコルを使用して、「設定可能」であるかどうかを確認してから、そのセットアップ方法を呼び出すことでした。これは非常に便利です。 "HomeTableViewCell"と "Blurb ..."の基本クラスで解決できますが、必要のないパラメータをキャストする必要があります – Sunkas

    +0

    はい、わかります。私の答えは、Nate Mannが提案したものに加えて、編集1で説明した問題を解決する方法です。もう一度、dataSouce宣言でジェネリックパラメータCell:CellDataSetupableを使用する必要がありますが、クラスを使用してインスタンス化具体的なオブジェクトでは、具体的な 'UITableViewCell'サブクラスを使用します。また、サブクラスが異なるタイプのセルでも動作するようにしたい場合は、サブクラスを汎用的にすることもできます。編集を参照してください。 – timaktimak

    +0

    私はInterface BuilderでUIViewオブジェクトの汎用サブクラスの作成がサポートされていないと私の質問で簡単に触れました。これを強調するために 'Edit 2'を追加しました。また別のSO質問へのリンクも付いています。私はすでに成功していない一般的なUITableViewCellクラスの具体的なサブクラスを作成しようとしました。 – Sunkas

    関連する問題