2017-06-16 65 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()即可。我不確定這是否是正式的方式,但它對我有用。