2017-06-16 64 views
0

我是初学iOS开发人员(这实际上是我第一次尝试iOS应用程序)。我制作的应用程序应该在地图上显示污染等级。我想要的是设置一个滑块并根据滑块设置在一定时间内显示污染等级。使用滑块过滤NSFetchResultsController并使用NSPredicate

我最初的,显然是错误的做法,是改变fetchResultsController获取请求谓词,只要滑块发生变化并更改func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)中的显示结果。这种方法的问题在于,更改提取请求似乎没有调用controllerDidChangeContent函数。看起来这只是在将新元素添加到数据库时触发的。什么是使这种动态改变获取请求的标准解决方案?

我出现在做上述工作的类下面。

CoreDataViewController

import UIKit 
import CoreData 

class CoreDataViewController: UIViewController, NSFetchedResultsControllerDelegate { 
    // MARK: Properties 

    var fetchedResultsController : NSFetchedResultsController<NSFetchRequestResult>? { 
     didSet { 
      // Whenever the frc changes, execute the search and 
      // reload the data 
      print("fetchedResultsController did set") 
      fetchedResultsController?.delegate = self 
      executeSearch() 
     } 
    } 

    // MARK: Initializers 

    init?(_ coder: NSCoder, fetchedResultsController fc : NSFetchedResultsController<NSFetchRequestResult>) { 
     fetchedResultsController = fc 
     super.init(coder: coder) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     super.init(coder: aDecoder) 
    } 
} 

extension CoreDataViewController { 

    func executeSearch() { 
     if let fc = fetchedResultsController { 
      do { 
       try fc.performFetch() 
      } catch let e as NSError { 
       print("Error while trying to perform a search: \n\(e)\n\(fetchedResultsController)") 
      } 
     } 
    } 
} 

的MapViewController(延伸CoreDataViewController)

import UIKit 
import GoogleMaps 
import CoreData 

class MapViewController: CoreDataViewController { 

// var mapView: GMSMapView! 
    @IBOutlet weak var testButton: UIBarButtonItem! 
    @IBOutlet weak var timestampSlider: UISlider! 

    override func loadView() { 

     // Get the stack 
     let delegate = UIApplication.shared.delegate as! AppDelegate 
     let stack = delegate.stack 

     // Create a fetchrequest 
     let fr = NSFetchRequest<NSFetchRequestResult>(entityName: "Measurement") 
     fr.sortDescriptors = [NSSortDescriptor(key: "fromDatetime", ascending: true)] 
     fr.predicate = NSPredicate(format: "fromDatetime = %@", argumentArray: [2]) 

     // Create the FetchedResultController 
     fetchedResultsController = NSFetchedResultsController(fetchRequest: fr, managedObjectContext: stack.context, 
                   sectionNameKeyPath: nil, cacheName: nil) 

     let measurements = fetchedResultsController?.fetchedObjects as! [Measurement] 

     // Create a GMSCameraPosition that tells the map to display the 
     // coordinate latitude: 50.0646501, longitude: 19.9449799 at zoom level 6. 
     let camera = GMSCameraPosition.camera(withLatitude: 50.0646501, longitude: 19.9449799, zoom: 14.0) 
     let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera) 
     view = mapView 

     _ = measurements.flatMap({(m: Measurement) -> Void in 
      print(m.fromDatetime) 
      let marker = GMSMarker() 
      marker.position = CLLocationCoordinate2D(latitude: m.latitude, longitude: m.longitude) 
      marker.title = "Measurement" 
      marker.snippet = String(format: "pm10: %f, pm25: %f", m.pm10, m.pm25) 
      marker.map = mapView 
     }) 

    } 

    override func viewWillAppear(_ animated: Bool) { 
     self.navigationController?.isToolbarHidden = true 

    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Do any additional setup after loading the view, typically from a nib. 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 


    @IBAction func testButtonAction(_ sender: Any) { 
     // Get the stack 
     let delegate = UIApplication.shared.delegate as! AppDelegate 
     let stack = delegate.stack 
     _ = Measurement(sensorId: 0, fromDatetime: 2, pm10: 0.6, pm25: 0.3, airQualityIndex: 0.8, latitude: 50.0626402, longitude: 19.9385001, source: "Airly", windDeg: 0.9, windSpeed: 23.0, insertInto: stack.context) 
    } 

    @IBAction func timestampSliderAction(_ sender: Any) { 
     if self.timestampSlider == nil { 
      return 
     } 
     let sliderValue = floor(self.timestampSlider.value) 
     print(sliderValue) 

     let delegate = UIApplication.shared.delegate as! AppDelegate 
     let stack = delegate.stack 
     let fr = NSFetchRequest<NSFetchRequestResult>(entityName: "Measurement") 
     fr.sortDescriptors = [NSSortDescriptor(key: "fromDatetime", ascending: true)] 
     fr.predicate = NSPredicate(format: "fromDatetime = %@", argumentArray: [sliderValue]) 
//   Create the FetchedResultController 
     fetchedResultsController = NSFetchedResultsController(fetchRequest: fr, managedObjectContext: stack.context, 
                   sectionNameKeyPath: nil, cacheName: nil) 

     // alternative solution without creating new NSFetchedResultsController 
//  fetchedResultsController?.fetchRequest.predicate = NSPredicate(format: "fromDatetime = %@", argumentArray: [sliderValue]) 
//  executeSearch() 
    } 

    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { 
     print("controllerDidChangeContent called") 
     let measurements = fetchedResultsController?.fetchedObjects as! [Measurement] 
     let mapView = view as! GMSMapView 
     _ = measurements.flatMap({(m: Measurement) -> Void in 
      let marker = GMSMarker() 
      marker.position = CLLocationCoordinate2D(latitude: m.latitude, longitude: m.longitude) 
      marker.title = "Measurement" 
      marker.snippet = String(format: "pm10: %f, pm25: %f", m.pm10, m.pm25) 
      marker.map = mapView 
     }) 
    } 
} 

回答

0

该溶液被证明是非常简单的。要触发controllerDidChangeContent,我只需在谓词更改后调用fetchedResultsController?.managedObjectContext.refreshAllObjects()即可。我不确定这是否是正式的方式,但它对我有用。