从多个视图控制器接收委托方法调用
Receive delegate method calls from multiple view controllers
我有一个 SplitViewController
的应用程序。 MasterViewController
是 UITableViewController
。 DetailViewController
是 UIViewController
和 MapView
.
我正在呼叫 API 以获取事件列表。在 tableView 中,我想显示事件的名称和地址。在 mapView 中,我想标记这些事件的位置。
我有一个名为 DataLoader
的 class,我在其中调用 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({ [=10=].startDate!.timeIntervalSince1970 < .startDate!.timeIntervalSince1970 })
self.delegate?.eventDataReceived(events, error: nil)
}
}
}
我有一个名为 DataLoaderDelegate
的委托和一个名为 func eventDataReceived(items: [Event]?, error: NSError?)
的方法,该方法在所有事件对象都插入数组后调用,因此我可以通过该方法传递它。
我需要在填充 tableView 的 ListViewController
和填充 mapView 的 MapViewController
中实现此方法。所以我在两个视图控制器中实现了 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()
}
}
}
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")
}
}
但这就是问题所在。仅执行 ListViewController
中的实现。我认为原因是我在 MapViewController
.
中创建了 DataLoader
class 的新实例
我应该怎么做才能在整个应用程序中拥有一个 DataLoader
实例并从所有视图控制器接收其委托方法调用?
我已经上传了一个演示项目到我的 Dropbox 如果你需要 运行 它以获得更好的想法。
谢谢。
这是 Singleton-pattern 的最佳实践案例
您的 DataLoader 应该实现一个静态 let 来保存对同一对象的引用。
static let sharedInstance = DataLoader()
当您想要引用始终相同的单例对象时。用 DataLoader.sharedInstance
调用它
然后确保始终使用 .sharedInstance,并且不要忘记擦除对 class 的标准初始化程序的任何调用,因为除非您以编程方式阻止此行为,否则它仍会创建新实例。
解释
仅插入单例常量(如上所示)不会使 DataLoader class 始终 return 单例实例。现有的初始化程序调用如下:
var myDataLoader = DataLoader()
每次调用时仍会实例化一个新对象。通过以下方式到达单例实例:
var mySingletonDataLoader = DataLoader.sharedInstance
您可以更改标准(非单例)初始值设定项,例如:
init() {
NSLog("No instances allowed, please use .sharedInstance for a singleton")
}
完整解决方案
单例只是解决整个问题的一块棋子。使用的委托模式非常适合将信息从一个对象发送到另一个对象,从而委托工作。
提问者的问题需要另一种从一个对象到许多其他对象的广播机制。因此他决定实施 Observer-pattern with NSNotificationCenter
我有一个 SplitViewController
的应用程序。 MasterViewController
是 UITableViewController
。 DetailViewController
是 UIViewController
和 MapView
.
我正在呼叫 API 以获取事件列表。在 tableView 中,我想显示事件的名称和地址。在 mapView 中,我想标记这些事件的位置。
我有一个名为 DataLoader
的 class,我在其中调用 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({ [=10=].startDate!.timeIntervalSince1970 < .startDate!.timeIntervalSince1970 })
self.delegate?.eventDataReceived(events, error: nil)
}
}
}
我有一个名为 DataLoaderDelegate
的委托和一个名为 func eventDataReceived(items: [Event]?, error: NSError?)
的方法,该方法在所有事件对象都插入数组后调用,因此我可以通过该方法传递它。
我需要在填充 tableView 的 ListViewController
和填充 mapView 的 MapViewController
中实现此方法。所以我在两个视图控制器中实现了 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()
}
}
}
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")
}
}
但这就是问题所在。仅执行 ListViewController
中的实现。我认为原因是我在 MapViewController
.
DataLoader
class 的新实例
我应该怎么做才能在整个应用程序中拥有一个 DataLoader
实例并从所有视图控制器接收其委托方法调用?
我已经上传了一个演示项目到我的 Dropbox 如果你需要 运行 它以获得更好的想法。
谢谢。
这是 Singleton-pattern 的最佳实践案例 您的 DataLoader 应该实现一个静态 let 来保存对同一对象的引用。
static let sharedInstance = DataLoader()
当您想要引用始终相同的单例对象时。用 DataLoader.sharedInstance
然后确保始终使用 .sharedInstance,并且不要忘记擦除对 class 的标准初始化程序的任何调用,因为除非您以编程方式阻止此行为,否则它仍会创建新实例。
解释
仅插入单例常量(如上所示)不会使 DataLoader class 始终 return 单例实例。现有的初始化程序调用如下:
var myDataLoader = DataLoader()
每次调用时仍会实例化一个新对象。通过以下方式到达单例实例:
var mySingletonDataLoader = DataLoader.sharedInstance
您可以更改标准(非单例)初始值设定项,例如:
init() {
NSLog("No instances allowed, please use .sharedInstance for a singleton")
}
完整解决方案
单例只是解决整个问题的一块棋子。使用的委托模式非常适合将信息从一个对象发送到另一个对象,从而委托工作。 提问者的问题需要另一种从一个对象到许多其他对象的广播机制。因此他决定实施 Observer-pattern with NSNotificationCenter