2014-09-11 21 views
18

Próbuję zachować moje kontrolery widoku czyste zgodnie z opisem w tym artykule objc.io Issue #1 Lighter View Controllers. Przetestowałem tę metodę w Objective-C i działa dobrze. Mam oddzielną klasę, która implementuje metody UITableViewDataSource.Oddzielanie źródła danych innej klasie w Swift

#import "TableDataSource.h" 

@interface TableDataSource() 

@property (nonatomic, strong) NSArray *items; 
@property (nonatomic, strong) NSString *cellIdentifier; 

@end 

@implementation TableDataSource 

- (id)initWithItems:(NSArray *)items cellIdentifier:(NSString *)cellIdentifier { 
    self = [super init]; 
    if (self) { 
     self.items = items; 
     self.cellIdentifier = cellIdentifier; 
    } 
    return self; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return self.items.count; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier forIndexPath:indexPath]; 

    cell.textLabel.text = self.items[indexPath.row]; 

    return cell; 
} 

@end 

ze sterownika Tableview, wszystko co musisz zrobić, to instancję instancji tej klasy i ustawić ją jako źródło danych tableview i działa idealnie.

self.dataSource = [[TableDataSource alloc] initWithItems:@[@"One", @"Two", @"Three"] cellIdentifier:@"Cell"]; 
self.tableView.dataSource = self.dataSource; 

Teraz próbuję zrobić to samo w Swift. Najpierw tutaj jest mój kod. Jest to prawie tłumaczenie kodu Objective-C powyżej.

import Foundation 
import UIKit 

public class TableDataSource: NSObject, UITableViewDataSource { 

    var items: [AnyObject] 
    var cellIdentifier: String 

    init(items: [AnyObject]!, cellIdentifier: String!) { 
     self.items = items 
     self.cellIdentifier = cellIdentifier 

     super.init() 
    } 

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

    public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell 

     cell.textLabel?.text = items[indexPath.row] as? String 

     return cell 
    } 

} 

I tak to nazywam.

let dataSource = TableDataSource(items: ["One", "Two", "Three"], cellIdentifier: "Cell") 
tableView.dataSource = dataSource 

Ale aplikacja ulega awarii z następującym błędem.

- [NSConcreteNotification tableView: numberOfRowsInSection:]: nierozpoznany selektor wysyłane do instancji

I sprawdzone metody init o TableDataSource oraz przedmioty i identyfikator komórki jest przekazywana w porządku. Musiałem zadeklarować metody UITableViewDataSource method public i usunąć słowo kluczowe override, w przeciwnym razie spowodowałoby to błędy czasu kompilacji.

Nie mam pojęcia, co się tutaj dzieje. Czy ktoś może mi pomóc?

Dziękuję.

+0

Czy możesz pokazać ślad stosu w momencie awarii? –

+2

Wygląda na to, że Twoje źródło danych nie zostało zachowane. Gdzie przechowujesz referencję? – jlehr

+3

@jlehr To był rzeczywiście problem. Nie magazynowałem tego! Zrobiłem właściwość 'var dataSource: TableDataSource!' I przypisałem ją do właściwości 'dataSource' widoku tableview i działa teraz :) Dziękuję. – Isuru

Odpowiedz

25

Utwórz właściwość dla źródła danych i użyj go ze swoim widokiem stołu.

class ViewController: UIViewController { 

    @IBOutlet weak var tableView: UITableView! 
    var dataSource:TableDataSource! 

    override func viewDidLoad() { 
    super.viewDidLoad() 

    dataSource = TableDataSource(items: ["One", "Two", "Three"], cellIdentifier: "Cell") 

    tableView.dataSource = dataSource 

    } 
} 
+2

czy możesz wkleić tutaj klasę TableDataSource? –

4

Użyłem poniższy kod, na bardziej ogólne podejście, jako try ..

import UIKit 

class CustomDataSource<ItemsType, CellType:UITableViewCell>: NSObject, UITableViewDataSource { 

    typealias ConfigureCellClosure = (_ item: ItemsType, _ cell: CellType) -> Void 
    private var items: [ItemsType] 
    private let identifier: String 
    private var configureCellClosure: ConfigureCellClosure 


    init(withData items: [ItemsType], andId identifier: String, withConfigBlock config:@escaping ConfigureCellClosure) { 

     self.identifier = identifier 
     self.items  = items 
     self.configureCellClosure = config 
    } 

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCell(withIdentifier: self.identifier, for: indexPath) as! CellType 
     configureCellClosure(items[indexPath.row], cell) 
     return cell 
    } 

    func item(at indexpath: IndexPath) -> ItemsType { 

     return items[indexpath.row] 
    } 

} 

Biorąc kontrolera

var dataSource: CustomDataSource<CellObject, CustomTableViewCell>? 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     dataSource = CustomDataSource<CellObject, CustomTableViewCell>(withData: customDataStore.customData(), andId: CustomTableViewCell.defaultReuseIdentifier) { (cellObject, cell) in 
      cell.configureCell(with: cellObject) 
     } 
     customTableView.dataSource = dataSource 

     // Do any additional setup after loading the view. 
} 

stosuje takie rozwiązanie w moim małym projekcie WorldCountriesSwift

+0

Umieściłem DataSource w oddzielnym pliku, ale nie wiem, jak "odsłuchać zdarzenie click i wysłać kliknięty element do DetailViewController". 'klasa' PicturesDataSource: NSObject, UITableViewDataSource { } '' wewnątrz PicturesDataSource "ujęć" i "navigationController" nie są dostępne i dlatego '' jeśli pozwalają detailViewController = storyboard.instantiateViewController (withIdentifier "PictureDetailView") tak jak? PictureDetailViewController & navigationController? .pushViewController (detailViewController, animated: true) '' 'nie działa. –

+0

Spróbuj dodać źródło danych .item (at: indexPath) w metodzie didselectrow kontrolera viewcontroller, a następnie spróbuj przedstawić szczegóły pictureVC.present (PictureDetailViewController .....) – anoop4real

0

Poszerzenie zaakceptowanej odpowiedzi o "ayalcinkaya", co tłumaczy jak ale nie dlaczego:

Najprawdopodobniej to, co się dzieje jest to, że TableDataSource jest zwalniane jako tableview.dataSource is a weak reference, dlatego tworząc własność rozwiązuje problemu, gdyż tworzy silne odniesienie i unika delegata DataSource bycie dealokowanym.

Powiązane problemy