Подтвердить что ты не робот

Разделение источника данных на другой класс в Swift

Я пытаюсь очистить контроллеры своего вида, как описано в этой статье objc.io Issue # 1. Я протестировал этот метод в Objective-C, и он отлично работает. У меня есть отдельный класс, который реализует методы UITableViewDataSource.

#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

В контроллере tableview все, что мне нужно сделать, это создать экземпляр этого класса и установить его как источник данных tableview, и он отлично работает.

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

Теперь я пытаюсь сделать то же самое в Swift. Сначала здесь мой код. Это в значительной степени перевод кода Objective-C выше.

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
    }

}

И я называю это так.

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

Но приложение вылетает со следующей ошибкой.

- [NSConcreteNotification tableView: numberOfRowsInSection:]: непризнанный селектор, отправленный в экземпляр

Я проверил метод init TableDataSource, и элементы и идентификатор ячейки передаются в порядке. Мне пришлось объявить методы UITableViewDataSource public и удалить ключевое слово override, иначе оно даст ошибки времени компиляции.

Я не знаю, что здесь происходит. Кто-нибудь может помочь мне?

Спасибо.

4b9b3361

Ответ 1

Создайте свойство источника данных и используйте его со своим табличным представлением.

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

Я использовал приведенный ниже код для более общего подхода, как попробовать.

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]
    }

}

В поле зрения контроллера

   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.
}

Используется этот подход в моем маленьком проекте WorldCountriesSwift

Ответ 3

Расширение принятого ответа на "ayalcinkaya", что объясняет как, но не почему:

Скорее всего, происходит то, что ваш TableDataSource освобождается как tableview.dataSource - слабая ссылка, поэтому создание свойства решает проблему, поскольку она создает сильную ссылку и исключает освобождение делегата dataSource.