2016-11-24 12 views
1

私は本当に好きなテーブルビューを作成しました。デリゲート関数tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)で素晴らしいアニメーションを再生します。アニメーションをUITableViewのサブクラスに構築する

問題は、コードベースの他の場所でその動作を複製する方法を見つけることができません。コードをコピーしてデリゲート関数内に貼り付け、DRYに違反することはありません。プロジェクト内のすべてのテーブルビューのアニメーションパラメータを1か所から調整できるようにしたい。

  1. まず私がUITableViewのサブクラスを書き込もうとしましたが、アニメーションはデリゲート関数で指定されているので、それは何も良いことだありません。サブクラスを独自のデリゲートにして、必要な振る舞いを定義することはできますが、他のデリゲート関数はそのビューを含むビューで使用できません。

  2. 私はUITableViewDelegateから継承するプロトコルを作成することを考えましたが、それらのうちの1つだけが異なっていても、プロトコルのすべてのtableview代理機能を再実装する必要があります。

  3. 私はwillDisplayCell関数をうまくしようと考えましたが、うまくいくとは思えませんでした。

どうすればこのようにすることができますか?この非常に特殊なケースでも、デリゲート関数のデフォルトの動作を独自のデリゲートとして設定することなく、より一般的なOOPの意味で両方とも意味がありますか?

編集:実際に写真を撮ることはできますが、それにもかかわらずリクエストに応じてスクリーンショットを撮ることはできません。 enter image description here

+0

画面の並べ替えをしてください。 – Sanjukta

+0

スクリーンショットが大いに役立つかどうかはわかりませんが、うまくいけば私はもっと明確にしようとしています。 – Erik

答えて

1

UITableViewをサブクラス化したり、デリゲートプロトコルをスウィズルしたり、拡張したりしないでください。

私が行うことは、デリゲートメソッドを実装してラップするクラスを作成することです。このクラスは独自の代理人を持つことができますが、私はむしろそれを呼び出すためにクロージャを与えます。

class TableViewDelegate: UITableViewDeleagte { 
    var willDisplayCell: ((UITableView, UITableViewCell, IndexPath) -> Void)? 
    let animator: Animator 
    weak var tableView: UITableView? { 
     didSet { 
      tableView?.delegate = self 
     } 
    } 
    init(tableView: UITableView, animator: Animator) { 
     self.animator = animator 
    } 

    func tableView(_ tableView: UITableView, willDisplay cell: UItableViewCell, forRowAt indexPath: IndexPath) { 
     animator.animate(cell) 
     willDisplayCell?(tableView, cell, indexPath) 
    } 
} 

完全な例:これは私がして、選択したデータオブジェクトを返すことができるようになると私は、1クラスのデリゲートとデータソースを実装し、実世界のプロジェクトで

protocol AnimatorType { 
    func animate(view: UIView) 
} 


class Animator: AnimatorType { 
    func animate(view: UIView) { 
     view.transform = CGAffineTransform(translationX: -30, y: 0) 

     UIView.animate(withDuration: 0.3, animations: { 
      view.transform = CGAffineTransform.identity 
     }) 
    } 
} 

class TableViewDataSource:NSObject, UITableViewDataSource { 

    let data = Array(0 ..< 30).map{ $0 * $0 } 
    init(tableView: UITableView) { 
     self.tableView = tableView 
     super.init() 
     tableView.dataSource = self 
    } 

    weak var tableView: UITableView? 

    func numberOfSections(in tableView: UITableView) -> Int { 
     return 1 
    } 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
     return data.count 
    } 


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCell(withIdentifier: "Cell1", for: indexPath) 
     let v = data[indexPath.row] 
     cell.textLabel?.text = "\(v)" 

     return cell 
    } 

    func object(at indexPath: IndexPath) -> Int{ 
     return self.data[indexPath.row] 
    } 
} 


class TableViewDelegate:NSObject, UITableViewDelegate { 

    var willDisplayCell: ((UITableView, UITableViewCell, IndexPath) -> Void)? 
    var didSelectCell: ((UITableView, IndexPath) -> Void)? 

    let animator: AnimatorType 
    weak var tableView: UITableView? 

    init(tableView: UITableView, animator: AnimatorType) { 
     self.tableView = tableView 
     self.animator = animator 
     super.init() 
     tableView.delegate = self 

    } 

    private var highestIndexPath = IndexPath(row: -1, section: 0) 

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 
     if indexPath.row > highestIndexPath.row { 
      animator.animate(view: cell) 
      highestIndexPath = indexPath 

     } 
     willDisplayCell?(tableView, cell, indexPath) 
    } 

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
     didSelectCell?(tableView, indexPath) 
    } 
} 


class ViewController: UIViewController { 

    var tableViewDataSource: TableViewDataSource? 
    var tableViewDelegate: TableViewDelegate? 
    let animator = Animator() 

    @IBOutlet weak var tableView: UITableView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     self.tableViewDataSource = TableViewDataSource(tableView: tableView) 
     self.tableViewDelegate = TableViewDelegate(tableView: tableView, animator: animator) 

     self.tableViewDelegate?.didSelectCell = { 
      [weak self] tableView, indexPath in 
      guard let `self` = self else { return } 
      print("selected: \(self.tableViewDataSource!.object(at: indexPath))") 
     } 
    } 
} 

シングルクロージャコール。実際には、OFAPopulatorと呼ばれるそのようなクラスを設計するための(objective-c)フレームワークを書いています。

+0

これは私が一緒に行くものだと思います。プロトコルをクラスとして再構成して継承できるようにすることは非常に賢明です。私はアニメーターをそれを完全に定義するのではなく変数にするという考えを盗むつもりです。あなたはしばらくの間これをしてきましたか? – Erik

+0

さて、私のプロのiOS開発者のキャリアはiPhoneOSの開発者のキャリアとして始まりました。しかし、まず第一に、このコードはSOLIDの原則を尊重しています。それらをチェック! – vikingosegundo

+0

BTW:高度に構成可能で、[* Composition-over-Inheritance *](https://en.wikipedia.org/wiki/Composition_over_inheritance)パラダイムに従うので、 'TableViewDelegate'をサブクラス化する必要はあまりありません。 。最高の柔軟性と再利用性を得るために、 'Animator'がプロトコルになります。 – vikingosegundo

1

作成UIViewControllerサブクラスのすべてのあなたのアニメーションtableViewsが継承:

class BaseAnimatedTableViewController: UIViewController, UICollectionViewDelegate { 
    var tableView: UITableView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     tableView = UITableView 
     //setupTableView 
     tableView.delegatge = self 
    } 
    //override delegate methods, that all subclasses should share 
} 

、各アニメーションのtableViewのためにそれをサブクラス:あなたは確かにもUITableViewController代わりのUIViewControllerをサブクラス化することができます

class AnimatedTableView1: BaseAnimatedTableViewController, UITableViewDataSource { 
     ... 
} 

class AnimatedTableView2: BaseAnimatedTableViewController, UITableViewDataSource { 
     ... 
} 

+0

これは、継承を取得するためにサブクラス化できるクラスにデリゲートメソッドをカプセル化することです。ありがとうございました!私はvikingosegundoの答えで行くつもりです。なぜなら、それはコントローラにビューを作る必要はないからですが、あなたも同じくらい良いです! – Erik

関連する問題