【问题标题】:Separating Data Source to another class in Swift将数据源分离到 Swift 中的另一个类
【发布时间】:2014-11-05 11:33:16
【问题描述】:

我正在努力保持我的视图控制器清洁,如本文objc.io Issue #1 Lighter View Controllers 中所述。我在 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:]:无法识别的选择器发送到实例

我检查了TableDataSourceinit 方法,并且项目和单元格标识符可以正常通过。我必须声明 UITableViewDataSource 方法 public 并删除 override 关键字,否则会产生编译时错误。

我不知道这里出了什么问题。谁能帮帮我?

谢谢。

【问题讨论】:

  • 你能在崩溃点显示堆栈跟踪吗?
  • 您的数据源似乎没有被保留。您将参考存储在哪里?
  • @jlehr 这确实是问题所在。我没有存储它!我创建了一个属性 var dataSource: TableDataSource! 并将其分配给 tableview 的 dataSource 属性,现在它可以工作了:) 谢谢。
  • 发生这种情况是因为UITableView(以及其他视图/控件)的delegatedataSource 属性在Swift 中被声明为unowned,因此您需要自己引用委托/数据源(如果不是视图控制器本身)。
  • @NateCook 当我检查 dataSource 属性的类型但不知道它是什么时,我确实看到了 unowned 的东西。感谢您澄清这一点。

标签: ios objective-c uitableview swift datasource


【解决方案1】:

用“ayalcinkaya”扩展接受的答案,它解释了如何而不是为什么

很可能发生的情况是您的 TableDataSource 被释放为 tableview.dataSource is a weak reference,这就是为什么创建属性可以解决问题,因为它创建了一个强引用并避免了 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

    【讨论】:

    • 我将 DataSource 放在单独的文件中,但不知道如何“监听点击事件并将点击的项目发送到 DetailViewController”。 class PicturesDataSource: NSObject, UITableViewDataSource{ } 在 PicturesDataSource "storyboard" & "navigationController" 内不可用,因此 if let detailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController &amp; navigationController?.pushViewController(detailViewController, animated: true) 不工作。
    • 尝试在viewcontroller的didselectrow方法中添加dataSource?.item(at:indexPath),然后尝试呈现细节pictureVC.present(PictureDetailViewController.....)
    【解决方案3】:

    为数据源创建一个属性并将其与您的 tableview 一起使用。

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

    【讨论】:

    • 你能把TableDataSource类贴在这里吗?
    猜你喜欢
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 2019-03-23
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 2011-05-15
    相关资源
    最近更新 更多