Ios 当数据源和委托与视图控制器分离时,如何将表视图的单击项发送到其他视图控制器

Ios 当数据源和委托与视图控制器分离时,如何将表视图的单击项发送到其他视图控制器,ios,swift,uitableview,Ios,Swift,Uitableview,我是一名来自android背景的iOS初学者,刚刚学习了table view(对我来说,这是一个android ListView)。我正在尝试将数据源和委托与视图控制器分离。我找到了一些关于如何执行此操作的教程,但仍在研究如何将单击的项目发送到另一个视图控制器。代码如下: class PictureTableViewController: UIViewController { @IBOutlet weak var pictureTableView: UITableView!

我是一名来自android背景的iOS初学者,刚刚学习了table view(对我来说,这是一个android ListView)。我正在尝试将数据源和委托与视图控制器分离。我找到了一些关于如何执行此操作的教程,但仍在研究如何将单击的项目发送到另一个视图控制器。代码如下:

class PictureTableViewController: UIViewController {


    @IBOutlet weak var pictureTableView: UITableView!

    private let picsDataSource: PicturesDataSource

    required init?(coder aDecoder: NSCoder) {
        self.picsDataSource = PicturesDataSource()
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        pictureTableView.dataSource = picsDataSource
        pictureTableView.reloadData()
        pictureTableView.delegate = picsDataSource
    }
}

class PicturesDataSource: NSObject, UITableViewDataSource, UITableViewDelegate{

    private var pictureModels = [PictureModel]()

    override init(){
        let picModelsDataController = PictureModelsDataController()
        pictureModels = picModelsDataController.pictureModels
    }


    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PictureCell.self)) as! PictureCell

        let picModel = pictureModels[indexPath.row]
        cell.pictureName = picModel.pictureName
        cell.imageItem = picModel.imageItem

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


        //1 - try loading the "Detail" view controller and typecasting it to be DetailViewController
        if let detailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController {

            //2 - success! set its selecteImage property
            detailViewController.selectedImgName = pictureModels[indexPath.row].pictureName

            //3 - now push it onto the navigation controller
            navigationController?.pushViewController(detailViewController, animated: true)
        }
    }

}     
在:func tableView(tableView:UITableView,didSelectRowAt indepath:indepath){}中出错
由于PicturesDataSource类中没有“storyboard”和“navigationController”,如何将单击的项目(图片名称)发送到DetailsViewController

有关于分离数据源和委托的StackOverflow答案,但并没有解决我的问题

使用:Xcode 8.3 beta 6


您试图坚持MVC,但您混淆了实际的数据源是什么

图片数据源
不是您的数据源
。它是告诉您的表如何设置自身的代码

PictureModelsDataController()
是从中获取实际填充该表的数据的源


您发布的所有代码应在同一类中:

class PictureTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

更改这些行:

pictureTableView.dataSource = picsDataSource
pictureTableView.delegate = picsDataSource

并删除:

private let picsDataSource: PicturesDataSource

required init?(coder aDecoder: NSCoder) {
    self.picsDataSource = PicturesDataSource()
    super.init(coder: aDecoder)
}

您可以在表视图事件处理程序中包含对主视图控制器的引用。下面是我从您的示例中导出的游乐场代码:

import UIKit

// MARK: - Model

struct Picture {
  let title: String
  let image: UIImage
}

struct PictureModelsDataSource {
  let pictures = [
    Picture(title: "exampleTitle", image: UIImage(named: "exampleImage")!),
    Picture(title: "exampleTitle", image: UIImage(named: "exampleImage")!)
  ]
}

// MARK - View

class PictureCell: UITableViewCell {
  @IBOutlet weak var pictureTitleLabel: UILabel!
  @IBOutlet weak var pictureImage: UIImageView!
}

// MARK: - Controller

class PictureTableViewController: UIViewController {

  // MARK: - Properties

  @IBOutlet weak var pictureTableView: UITableView!

  private var pictureListController: PictureListController?

  // MARK: - View lifecycle

  override func viewDidLoad() {
    super.viewDidLoad()
    pictureListController = PictureListController()
    pictureListController?.viewController = self
    pictureTableView.dataSource = pictureListController
    pictureTableView.delegate = pictureListController
    pictureTableView.reloadData()
  }
}

class PictureDetailViewController: UIViewController {
  var selectedPictureTitle: String?
}

class PictureListController: NSObject, UITableViewDataSource, UITableViewDelegate {

  // MARK: - Properties

  weak var viewController: PictureTableViewController?

  private let pictures: [Picture] = {
    let pictureModelsDataSource = PictureModelsDataSource()
    return pictureModelsDataSource.pictures
  }()

  // MARK: - View setup

  func numberOfSections(in tableView: UITableView) -> Int {
    return 1
  }

  // MARK: - Event handling

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

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PictureCell.self)) as? PictureCell else {
      return UITableViewCell()
    }

    let picture = pictures[indexPath.row]
    cell.pictureTitleLabel.text = picture.title
    cell.pictureImage.image = picture.image

    return cell
  }

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let pictureTitle = pictures[indexPath.row].title
    let storyboard = UIStoryboard(name: "exampleStoryboard", bundle: nil)
    if let pictureDetailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController {
      pictureDetailViewController.selectedPictureTitle = pictureTitle
      viewController?.navigationController?.pushViewController(pictureDetailViewController, animated: true)
    }
  }
}

请参见
情节提要
使用此

let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
现在,您的第二个问题是关于如何获取导航控制器。这意味着在您的情况下如何获取
currentViewController
。这可以通过下面的代码得到

 func getCurrentViewController() -> UIViewController? {

 if let rootController = UIApplication.shared.keyWindow?.rootViewController {
    var currentController: UIViewController! = rootController
       while( currentController.presentedViewController != nil ) {
                currentController = currentController.presentedViewController
            }
            return currentController
        }
        return nil
    }
现在,您的
didSelectRowAt
代码将如下所示

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
 let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)

 if let detailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController 
   detailViewController.selectedImgName = pictureModels[indexPath.row].pictureName
    self.getCurrentViewController()!.pushViewController(detailViewController, animated: true)  
}

为什么故事板不可用?您可以使用此
let storyBoard:UIStoryboard=UIStoryboard(名称:“Main”,bundle:nil)
@Aditya,那么navigationController呢?发布了我的答案,希望对您有用我理解您的建议。我在Android上做了这件事(当需要的时候)。但我想在数据源中处理click事件,或者为委托创建另一个类(即PictureTableDelegate)并在那里处理click事件(在PictureTableDelegate中)。除了委托模式,还有其他方式吗?我已经根据你的评论编辑了我的帖子@Hassanta可以工作,但不像Android那样优雅。在Android中,我们使用列表适配器&通过构造函数将活动引用(this)传递给适配器,因此列表适配器中的所有内容都可用。是否可以通过构造函数将视图控制器的引用传递给数据源而不是属性(您是通过属性传递的)如果我将所有代码从PictureModelsDataController传递给PicturesDataSource呢?没关系。这只是视图控制器包含tableview的一点,因此视图控制器是tableview的数据源及其委托。填充表的实际数据来自自定义类。这是一个非常低质量的答案。不起作用
getCurrentViewController()
返回nil。@HassanTareq我在我的项目中使用相同的方法,它工作正常
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
 let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)

 if let detailViewController = storyboard.instantiateViewController(withIdentifier: "PictureDetailView") as? PictureDetailViewController 
   detailViewController.selectedImgName = pictureModels[indexPath.row].pictureName
    self.getCurrentViewController()!.pushViewController(detailViewController, animated: true)  
}