2016-11-24 22 views
1

我創建了一個我非常喜歡的表視圖 - 它在代理函數tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)中播放了一些很好的動畫。將動畫構建爲UITableView的子類

問題是,我找不到在我的代碼庫中的其他地方複製該行爲的方法,無需在代理函數內粘貼代碼並違反DRY。我希望能夠從一個地方調整項目中所有表格的動畫參數。

  1. 首先,我試着寫的UITableView一個子類,但那是因爲動畫在一個委託函數指定白搭。你可以使子類成爲自己的委託並定義你想要的行爲,但是它的其他委託函數都不可用於其包含的視圖。

  2. 我認爲寫了一個從UITableViewDelegate繼承的協議,但是你必須重新實現協議中的每個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) 
    } 
} 

一個完整的例子:

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))") 
     } 
    } 
} 

在實際項目中,我將實現在一個類中的委託和數據源,因爲這將讓我有一個返回選定數據對象單封閉呼叫。的確我寫了一個(objective-c)框架來設計這樣的類,叫做OFAPopulator

+0

我認爲這是我會一起去的。將協議重新轉換爲類可以繼承它,這非常巧妙。我會偷走你的想法,讓動畫師變成一個變量,而不是直接定義它。你已經這樣做了一段時間了嗎? – Erik

+0

嗯,我的專業iOS開發人員職業生涯始於iPhoneOS開發人員職業。但首先,這些代碼尊重SOLID原則 - 我爲每個編寫的項目遵循這些原則。去看一下! – vikingosegundo

+0

順便說一下:應該不需要太多的子類'TableViewDelegate',因爲它是高度可配置的,並遵循[*構圖 - 繼承*](https://en.wikipedia.org/wiki/Composition_over_inheritance)範式。爲了獲得最高的靈活性和可重用性,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:

class AnimatedTableView1: BaseAnimatedTableViewController, UITableViewDataSource { 
     ... 
} 

class AnimatedTableView2: BaseAnimatedTableViewController, UITableViewDataSource { 
     ... 
} 

你當然也可以繼承UITableViewController,而不是UIViewController

+0

所以技巧就是將委託方法封裝在一個可以被繼承的子類中。謝謝!我打算用vikingosegundo的答案,因爲它不需要把視圖變成一個控制器,但你的一樣好! – Erik