Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用MVP模式iOS将数据传递给另一个控制器_Ios_Swift_Mvp - Fatal编程技术网

使用MVP模式iOS将数据传递给另一个控制器

使用MVP模式iOS将数据传递给另一个控制器,ios,swift,mvp,Ios,Swift,Mvp,我使用的是MVP设计模式。我有两种方法将数据传递给另一个视图控制器,我将在下面提到。我不知道哪一个是正确的,也不违反MVP模式。我知道这是一个很大的问题,但它确实非常重要 1) 在下面使用带演示者的init,我通过传递视图控制器所需的演示者来创建视图控制器 struct HotelTemplate { var id: String var name: String var icon: String } class ListHotelPresenter: NSObject

我使用的是MVP设计模式。我有两种方法将数据传递给另一个视图控制器,我将在下面提到。我不知道哪一个是正确的,也不违反MVP模式。我知道这是一个很大的问题,但它确实非常重要

1) 在下面使用带演示者的
init
,我通过传递视图控制器所需的演示者来创建视图控制器

struct HotelTemplate {
    var id: String
    var name: String
    var icon: String
}

class ListHotelPresenter: NSObject {

    private var data = [HotelTemplate]()

    func getPresenter(_ index: Int) -> HotelDetailsPresenter {
        let presenter = HotelDetailsPresenter(id: data[index].id, name: data[index].name, icon: data[index].icon)
        return presenter
    }
}

// InitialViewController
class ListHotelViewController: UIViewController {

    class func `init`(with presenter: ListHotelPresenter) -> ListHotelViewController {
        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ListHotelViewController") as! ListHotelViewController
        vc.presenter = presenter
        return vc
    }

    var presenter: ListHotelPresenter!

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let detailPresenter = presenter.getPresenter(indexPath.row)
        let vc = HotelDetailsViewController.init(with: detailPresenter)
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

// ViewController that will be push
class HotelDetailsViewController: UIViewController {

    class func `init`(with presenter: HotelDetailsPresenter) -> HotelDetailsViewController {
        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HotelDetailsViewController") as! HotelDetailsViewController
        return vc
    }

    var presenter: HotelDetailsPresenter!

    override func viewDidLoad() {
        super.viewDidLoad()

        presenter.loadHoteData()
    }
}

class HotelDetailsPresenter: NSObject {
    var hotelId: String
    var hotelName: String
    var hotelIcon: String

    init(id: String, name: String, icon: String) {
        self.hotelId = id
        self.hotelName = name
        self.hotelIcon = icon
    }

    func loadHoteData() {
        // Load hotel data.
        // Alamofire.request ..................
    }
}
2) 通过发送
id、名称、图标
,然后在
viewDidLoad()中初始化演示者

因此,以下是我关注的问题:

1) 哪一个是正确的?(我觉得第一种方法非常干净,但我的上级告诉我它违反了MVP模式。我不知道怎么做。)

2) 控制器的presenter属性应该是公共的还是私有的?

嘿,根据苹果的指导,两者都可以,但是第二个属性对第一个属性来说更易读,更容易理解,但有一些更改,请检查代码

class ListHotelViewController: UIViewController{
private var presenter: ListHotelPresenter!

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HotelDetailsViewController") as! HotelDetailsViewController
    vc.objHotelTemplate = presenter.getHotelTemplate(indexPath.row)
    self.navigationController?.pushViewController(vc, animated: true)
}
}
class HotelDetailsViewController: UIViewController {

private var presenter: HotelDetailsPresenter!
var objHotelTemplate:HotelTemplate!

override func viewDidLoad() {
    super.viewDidLoad()
    presenter = HotelDetailsPresenter(id: objHotelTemplate.id, name: objHotelTemplate.name, icon: objHotelTemplate.icon)
    presenter.loadHoteData()
}
}

嘿,根据苹果公司的指导,两者都可以,但第二个版本对第一个版本更具可读性,也更容易理解,但有一些改动,请检查代码

class ListHotelViewController: UIViewController{
private var presenter: ListHotelPresenter!

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "HotelDetailsViewController") as! HotelDetailsViewController
    vc.objHotelTemplate = presenter.getHotelTemplate(indexPath.row)
    self.navigationController?.pushViewController(vc, animated: true)
}
}
class HotelDetailsViewController: UIViewController {

private var presenter: HotelDetailsPresenter!
var objHotelTemplate:HotelTemplate!

override func viewDidLoad() {
    super.viewDidLoad()
    presenter = HotelDetailsPresenter(id: objHotelTemplate.id, name: objHotelTemplate.name, icon: objHotelTemplate.icon)
    presenter.loadHoteData()
}

}

在Objective-C中,您可以看到一个模型的视图。我们可以在视图控制器的头文件中向前声明模型:

@class HotelTemplate;
在.m文件中,我确保不导入HotelTemplate.h“”。这样,模型仍然不透明。你可以传来传去,但你不能看里面

我不知道有什么办法可以在Swift中强制执行。因此,让我按照您的示例传递下一个演示者,而不是下一个模型。我们所需要的只是在
viewDidLoad()
中设置每个演示者的视图。为了防止保留循环,这将是一个弱属性

首先,这里是列表视图控制器。我做了一个UITableViewController

final class ListHotelViewController: UITableViewController {
    private var presenter = ListHotelPresenter()

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.loadHotelData()
    }
}
final class HotelDetailsViewController: UIViewController {
    private var presenter: HotelDetailsPresenter!
    @IBOutlet private var textLabel: UILabel!

    static func `init`(with presenter: HotelDetailsPresenter) -> HotelDetailsViewController {
        let vc = UIStoryboard(name: "Main", bundle: nil)
                .instantiateViewController(withIdentifier: "HotelDetailsViewController")
                as! HotelDetailsViewController
        vc.presenter = presenter
        return vc
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.show()
    }
}
演示者将通过协议将其回调:

protocol ListHotelView: class {
    func redraw()
    func showDetails(nextPresenter: HotelDetailsPresenter)
}

extension ListHotelViewController: ListHotelView {
    func redraw() {
        tableView.reloadData()
    }

    func showDetails(nextPresenter: HotelDetailsPresenter) {
        let vc = HotelDetailsViewController.init(with: nextPresenter)
        navigationController?.pushViewController(vc, animated: true)
    }
}
protocol HotelDetailsView: class {
    func show(name: String, icon: String)
    func showDetails(location: String)
}

extension HotelDetailsViewController: HotelDetailsView {
    func show(name: String, icon: String) {
        textLabel?.text = name
        // Do something to show icon
    }

    func showDetails(location: String) {
        // Show other hotel details we loaded
    }
}
以下是表视图的数据源和委托:

extension ListHotelViewController /* UITableViewDataSource */ {
    public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return presenter.hotelCount
    }

    public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Hotel", for: indexPath) as! HotelTableViewCell
        presenter.configure(cell: cell, row: indexPath.row)
        return cell
    }
}

extension ListHotelViewController /* UITableViewDelegate */ {
    public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        presenter.showDetails(row: indexPath.row)
    }
}
在每一步中,它都服从于演示者。演示者具有返回视图的弱链接,但仅通过协议。它不知道该视图是ListHotelViewController。对于基于终端的接口,我们应该能够用一堆
print(41;
语句来实现视图

final class ListHotelPresenter {
    private weak var view: ListHotelView?
    private var model: [HotelTemplate] = [] {
        didSet {
            view?.redraw()
        }
    }

    var hotelCount: Int {
        return model.count
    }

    func setView(_ view: ListHotelView) {
        self.view = view
    }

    func loadHotelData() {
        // Network request to load data into model. Let's pretend with dummy data:
        let hilton = HotelTemplate(id: "hilton", name: "Hilton", icon: "H")
        let radisson = HotelTemplate(id: "radisson", name: "Radisson", icon: "R")
        model = [hilton, radisson]
    }

    func configure(cell: HotelCell, row: Int) {
        let hotel = model[row]
        cell.show(name: hotel.name, icon: hotel.icon)
    }

    func showDetails(row: Int) {
        let nextPresenter = HotelDetailsPresenter(summaryModel: model[row])
        view?.showDetails(nextPresenter: nextPresenter)
    }
}
配置(单元格:行:)
中,演示者与给定单元格对话。请注意,该单元也是一个协议。有了MVP,我真的试着想象我将如何使用它来制作一个基于终端的界面。这是手机:

protocol HotelCell: class {
    func show(name: String, icon: String)
}

final class HotelTableViewCell: UITableViewCell {}

extension HotelTableViewCell: HotelCell {
    func show(name: String, icon: String) {
        textLabel?.text = name
        // Do something to show icon
    }
}
实际上,您应该向“表视图”单元格添加更多内容。在这个示例中,我只使用了一个普通单元格及其文本标签

最后,我们来看看推式视图控制器

final class ListHotelViewController: UITableViewController {
    private var presenter = ListHotelPresenter()

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.loadHotelData()
    }
}
final class HotelDetailsViewController: UIViewController {
    private var presenter: HotelDetailsPresenter!
    @IBOutlet private var textLabel: UILabel!

    static func `init`(with presenter: HotelDetailsPresenter) -> HotelDetailsViewController {
        let vc = UIStoryboard(name: "Main", bundle: nil)
                .instantiateViewController(withIdentifier: "HotelDetailsViewController")
                as! HotelDetailsViewController
        vc.presenter = presenter
        return vc
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.show()
    }
}
我假设我们将立即显示我们拥有的摘要信息,但web服务中还有更多细节。这是由该演示者完成的

struct HotelDetails {
    let location: String
    // more details…
}

final class HotelDetailsPresenter {
    private weak var view: HotelDetailsView?
    private let summaryModel: HotelTemplate        
    private var detailsModel: HotelDetails? {
        didSet {
            guard let detailsModel = detailsModel else { return }
            view?.showDetails(location: detailsModel.location)
        }
    }

    init(summaryModel: HotelTemplate) {
        self.summaryModel = summaryModel
    }

    func setView(_ view: HotelDetailsView) {
        self.view = view
    }

    func show() {
        view?.show(name: summaryModel.name, icon: summaryModel.icon)
        // Network request to load data into detailsModel
    }
}
与往常一样,演示者通过协议告诉视图要做什么:

protocol ListHotelView: class {
    func redraw()
    func showDetails(nextPresenter: HotelDetailsPresenter)
}

extension ListHotelViewController: ListHotelView {
    func redraw() {
        tableView.reloadData()
    }

    func showDetails(nextPresenter: HotelDetailsPresenter) {
        let vc = HotelDetailsViewController.init(with: nextPresenter)
        navigationController?.pushViewController(vc, animated: true)
    }
}
protocol HotelDetailsView: class {
    func show(name: String, icon: String)
    func showDetails(location: String)
}

extension HotelDetailsViewController: HotelDetailsView {
    func show(name: String, icon: String) {
        textLabel?.text = name
        // Do something to show icon
    }

    func showDetails(location: String) {
        // Show other hotel details we loaded
    }
}

如您所见,这些属性是私有的。为了支持单元测试,我们可能需要放宽使用
private(set)
的限制,以便只有setter是私有的。

在Objective-C中,您可以看到传递模型的视图。我们可以在视图控制器的头文件中向前声明模型:

@class HotelTemplate;
在.m文件中,我确保不导入HotelTemplate.h“”。这样,模型仍然不透明。你可以传来传去,但你不能看里面

我不知道有什么办法可以在Swift中强制执行。因此,让我按照您的示例传递下一个演示者,而不是下一个模型。我们所需要的只是在
viewDidLoad()
中设置每个演示者的视图。为了防止保留循环,这将是一个弱属性

首先,这里是列表视图控制器。我做了一个UITableViewController

final class ListHotelViewController: UITableViewController {
    private var presenter = ListHotelPresenter()

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.loadHotelData()
    }
}
final class HotelDetailsViewController: UIViewController {
    private var presenter: HotelDetailsPresenter!
    @IBOutlet private var textLabel: UILabel!

    static func `init`(with presenter: HotelDetailsPresenter) -> HotelDetailsViewController {
        let vc = UIStoryboard(name: "Main", bundle: nil)
                .instantiateViewController(withIdentifier: "HotelDetailsViewController")
                as! HotelDetailsViewController
        vc.presenter = presenter
        return vc
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.show()
    }
}
演示者将通过协议将其回调:

protocol ListHotelView: class {
    func redraw()
    func showDetails(nextPresenter: HotelDetailsPresenter)
}

extension ListHotelViewController: ListHotelView {
    func redraw() {
        tableView.reloadData()
    }

    func showDetails(nextPresenter: HotelDetailsPresenter) {
        let vc = HotelDetailsViewController.init(with: nextPresenter)
        navigationController?.pushViewController(vc, animated: true)
    }
}
protocol HotelDetailsView: class {
    func show(name: String, icon: String)
    func showDetails(location: String)
}

extension HotelDetailsViewController: HotelDetailsView {
    func show(name: String, icon: String) {
        textLabel?.text = name
        // Do something to show icon
    }

    func showDetails(location: String) {
        // Show other hotel details we loaded
    }
}
以下是表视图的数据源和委托:

extension ListHotelViewController /* UITableViewDataSource */ {
    public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return presenter.hotelCount
    }

    public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Hotel", for: indexPath) as! HotelTableViewCell
        presenter.configure(cell: cell, row: indexPath.row)
        return cell
    }
}

extension ListHotelViewController /* UITableViewDelegate */ {
    public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        presenter.showDetails(row: indexPath.row)
    }
}
在每一步中,它都服从于演示者。演示者具有返回视图的弱链接,但仅通过协议。它不知道该视图是ListHotelViewController。对于基于终端的接口,我们应该能够用一堆
print(41;
语句来实现视图

final class ListHotelPresenter {
    private weak var view: ListHotelView?
    private var model: [HotelTemplate] = [] {
        didSet {
            view?.redraw()
        }
    }

    var hotelCount: Int {
        return model.count
    }

    func setView(_ view: ListHotelView) {
        self.view = view
    }

    func loadHotelData() {
        // Network request to load data into model. Let's pretend with dummy data:
        let hilton = HotelTemplate(id: "hilton", name: "Hilton", icon: "H")
        let radisson = HotelTemplate(id: "radisson", name: "Radisson", icon: "R")
        model = [hilton, radisson]
    }

    func configure(cell: HotelCell, row: Int) {
        let hotel = model[row]
        cell.show(name: hotel.name, icon: hotel.icon)
    }

    func showDetails(row: Int) {
        let nextPresenter = HotelDetailsPresenter(summaryModel: model[row])
        view?.showDetails(nextPresenter: nextPresenter)
    }
}
配置(单元格:行:)
中,演示者与给定单元格对话。请注意,该单元也是一个协议。有了MVP,我真的试着想象我将如何使用它来制作一个基于终端的界面。这是手机:

protocol HotelCell: class {
    func show(name: String, icon: String)
}

final class HotelTableViewCell: UITableViewCell {}

extension HotelTableViewCell: HotelCell {
    func show(name: String, icon: String) {
        textLabel?.text = name
        // Do something to show icon
    }
}
实际上,您应该向“表视图”单元格添加更多内容。在这个示例中,我只使用了一个普通单元格及其文本标签

最后,我们来看看推式视图控制器

final class ListHotelViewController: UITableViewController {
    private var presenter = ListHotelPresenter()

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.loadHotelData()
    }
}
final class HotelDetailsViewController: UIViewController {
    private var presenter: HotelDetailsPresenter!
    @IBOutlet private var textLabel: UILabel!

    static func `init`(with presenter: HotelDetailsPresenter) -> HotelDetailsViewController {
        let vc = UIStoryboard(name: "Main", bundle: nil)
                .instantiateViewController(withIdentifier: "HotelDetailsViewController")
                as! HotelDetailsViewController
        vc.presenter = presenter
        return vc
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        presenter.setView(self)
        presenter.show()
    }
}
我假设我们将立即显示我们拥有的摘要信息,但web服务中还有更多细节。这是由该演示者完成的

struct HotelDetails {
    let location: String
    // more details…
}

final class HotelDetailsPresenter {
    private weak var view: HotelDetailsView?
    private let summaryModel: HotelTemplate        
    private var detailsModel: HotelDetails? {
        didSet {
            guard let detailsModel = detailsModel else { return }
            view?.showDetails(location: detailsModel.location)
        }
    }

    init(summaryModel: HotelTemplate) {
        self.summaryModel = summaryModel
    }

    func setView(_ view: HotelDetailsView) {
        self.view = view
    }

    func show() {
        view?.show(name: summaryModel.name, icon: summaryModel.icon)
        // Network request to load data into detailsModel
    }
}
与往常一样,演示者通过协议告诉视图要做什么:

protocol ListHotelView: class {
    func redraw()
    func showDetails(nextPresenter: HotelDetailsPresenter)
}

extension ListHotelViewController: ListHotelView {
    func redraw() {
        tableView.reloadData()
    }

    func showDetails(nextPresenter: HotelDetailsPresenter) {
        let vc = HotelDetailsViewController.init(with: nextPresenter)
        navigationController?.pushViewController(vc, animated: true)
    }
}
protocol HotelDetailsView: class {
    func show(name: String, icon: String)
    func showDetails(location: String)
}

extension HotelDetailsViewController: HotelDetailsView {
    func show(name: String, icon: String) {
        textLabel?.text = name
        // Do something to show icon
    }

    func showDetails(location: String) {
        // Show other hotel details we loaded
    }
}

如您所见,这些属性是私有的。为了支持单元测试,我们可能需要放宽使用
private(set)
的限制,以便只有setter是私有的。

我不再使用
Model-View-Controller
。我正在使用
模型视图演示器
。因此,我的视图不应该知道模型。我不再使用
Model-View-Controller
。我正在使用
模型视图演示器
。所以我的观点不应该知道模型。