ios - clases - herencia en swift 4
Separando el origen de datos a otra clase en Swift (3)
Cree una propiedad para el origen de datos y utilícela con su vista de tabla.
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
}
}
Estoy tratando de mantener mis controladores de vista limpios como se describe en este artículo objc.io Problema # 1 Controladores de vista más claros . Probé este método en Objective-C y funciona bien. Tengo una clase separada que implementa métodos 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
Desde el controlador de Tableview, todo lo que tengo que hacer es crear una instancia de esta clase y establecerla como la fuente de datos de la vista de tabla y funciona perfectamente.
self.dataSource = [[TableDataSource alloc] initWithItems:@[@"One", @"Two", @"Three"] cellIdentifier:@"Cell"];
self.tableView.dataSource = self.dataSource;
Ahora estoy tratando de hacer lo mismo en Swift. Primero aquí está mi código. Es casi una traducción del código Objective-C de arriba.
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
}
}
Y lo llamo así.
let dataSource = TableDataSource(items: ["One", "Two", "Three"], cellIdentifier: "Cell")
tableView.dataSource = dataSource
Pero la aplicación se bloquea con el siguiente error.
- [NSConcreteNotification tableView: numberOfRowsInSection:]: selector no reconocido enviado a la instancia
TableDataSource
método de TableDataSource
de TableDataSource
y los elementos y el identificador de celda se pasa bien. Tuve que declarar UITableViewDataSource
métodos de UITableViewDataSource
y eliminar la palabra clave de override
contrario daría errores de compilación.
No tengo ni idea de lo que está mal aquí. ¿Por favor, puede alguien ayudarme?
Gracias.
Extendiendo la respuesta aceptada por "ayalcinkaya", que explica el cómo pero no el por qué :
Lo más probable es que lo que está sucediendo es que su TableDataSource está siendo desasignado como tableview.dataSource es una referencia débil , es por eso que crear una propiedad resuelve el problema, ya que crea una referencia fuerte y evita que el delegado dataSource se desasigne.
Utilicé el siguiente código, para un enfoque más genérico, como un intento ...
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]
}
}
En vista controlador
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.
}
Usé este enfoque en mi pequeño proyecto WorldCountriesSwift