从多个视图控制器接收委托方法调用

Isu*_*uru 5 delegates uiviewcontroller uisplitviewcontroller ios swift

我有一个带有的应用SplitViewController。该MasterViewController是UITableViewController。并且DetailViewController是UIViewController带有的MapView。

在此处输入图片说明

我正在调用API以获取事件列表。在tableView中,我想显示事件的名称和地址。在mapView中,我要标记这些事件的位置。

我有一个名为的类,DataLoader在其中调用API,解析JSON对象并创建Event对象并将它们成功放入数组中。

import Foundation
import SwiftyJSON

@objc protocol DataLoaderDelegate {
    func eventDataReceived(items: [Event]?, error: NSError?)
}

public class DataLoader {

    private let api = ApiClient()

    var delegate: DataLoaderDelegate?

    init() { }

    public func fetchEvents() {
        api.getEvents({ (data) -> Void in
            let json = JSON(data)
            self.processEventData(json["data"])
        }, failure: { (error) -> Void in
            println("Error fetching events: \(error?.localizedDescription)")
            self.delegate?.eventDataReceived(nil, error: error)
        })
    }

    private func processEventData(data: JSON) {
        var events = [Event]()

        if let eventsArray = data.array {
            for eventObj in eventsArray {
                let event = Event(
                    id: eventObj["id"].int!,
                    type: EventType(rawValue: eventObj["type"].int!)!,
                    location: eventObj["location"].string!,
                    status: EventStatus(rawValue: eventObj["status"].int!)!
                )
                event.allDay = eventObj["allDay"].int!
                event.title = eventObj["title"].string
                event.description = eventObj["description"].string
                event.latitude = eventObj["lat"].double
                event.longitude = eventObj["lng"].double
                event.startDate = NSDate(string: eventObj["start"].string!)
                event.endDate = NSDate(string: eventObj["end"].string!)
                event.createdAtDate = NSDate(string: eventObj["created_at"].string!)
                event.updatedAtDate = NSDate(string: eventObj["updated_at"].string!)
                event.creatorEmail = eventObj["creatorEmail"].string
                event.organizerEmail = eventObj["organizerEmail"].string
                event.googleCalendarId = eventObj["google_cal_id"].string!
                event.googleEventId = eventObj["google_event_id"].string!
                event.htmlLink = eventObj["htmlLink"].string!

                events.append(event)
            }

            // Order the events by the startDate value
            events.sort({ $0.startDate!.timeIntervalSince1970 < $1.startDate!.timeIntervalSince1970 })

            self.delegate?.eventDataReceived(events, error: nil)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我有一个DataLoaderDelegate使用方法调用的委托,该方法func eventDataReceived(items: [Event]?, error: NSError?)在将所有事件对象插入数组后调用,因此我可以将其传递给该方法。

我需要在ListViewController填充tableView的地方和MapViewController填充mapView 的地方都实现此方法。所以我已经eventDataReceived在两个视图控制器中实现了委托方法,以便获取事件对象。

ListViewController.swift

import UIKit

class ListViewController: UITableViewController, DataLoaderDelegate {

    let loader = DataLoader()

    private var events = [Event]()

    override func viewDidLoad() {
        super.viewDidLoad()

        loader.delegate = self
        loader.fetchEvents()
    }

    // MARK: - Table view data source
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return events.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell

        let event = events[indexPath.row]
        cell.textLabel?.text = event.title
        cell.detailTextLabel?.text = event.location

        return cell
    }

    // MARK: - DataLoaderDelegate
    func eventDataReceived(items: [Event]?, error: NSError?) {
        println("ListViewController got \(items?.count) events")
        if let events = items {
            self.events = events
            tableView.reloadData()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

MapViewController.swift

import UIKit

class MapViewController: UIViewController, DataLoaderDelegate {

    let loader = DataLoader()

    override func viewDidLoad() {
        super.viewDidLoad()

        loader.delegate = self
    }

    // MARK: - DataLoaderDelegate
    func eventDataReceived(items: [Event]?, error: NSError?) {
        println("MapViewController got \(items?.count) events")
    }
}
Run Code Online (Sandbox Code Playgroud)

但这就是问题所在。仅执行中的实现ListViewController。我认为原因是我在中创建了DataLoaderclass 的新实例MapViewController。

我应该怎么做才能DataLoader在整个应用程序中拥有一个实例,并从所有视图控制器接收其委托方法调用?

如果您需要运行一个演示项目以获得更好的主意,那么我已经将演示项目上传到了我的Dropbox。

谢谢。

gut*_*uhu 5

这是单例模式的最佳实践案例 您的 DataLoader 应该实现一个静态 let 保存对 self 的同一对象的引用。

static let sharedInstance = DataLoader()
Run Code Online (Sandbox Code Playgroud)

当您希望引用始终相同的单例对象时。调用它DataLoader.sharedInstance

确保始终使用 .sharedInstance 然后不要忘记擦除类的标准初始化程序的任何调用,因为除非您以编程方式阻止此行为,否则它仍然会创建新实例。

解释

仅插入单例常量(如上所示)不会使 DataLoader 类始终返回单例实例。现有的初始化程序调用如下:

   var myDataLoader = DataLoader()
Run Code Online (Sandbox Code Playgroud)

每次调用它们时仍然会实例化一个新对象。单例实例是通过以下方式到达的:

   var mySingletonDataLoader = DataLoader.sharedInstance
Run Code Online (Sandbox Code Playgroud)

您可以更改标准(非单例)初始值设定项,例如:

init() {
NSLog("No instances allowed, please use .sharedInstance for a singleton")
}
Run Code Online (Sandbox Code Playgroud)

完整的解决方案

单例只是解决整个问题的一小部分。使用的委托模式非常适合将信息从一个对象发送到另一个对象,从而委托工作。提问者的问题需要另一种从一个对象到许多其他对象的广播机制。因此他决定使用NSNotificationCenter实现观察者模式