使用核心数据,我得到一个错误'nib,但没有得到UICollectionView。

时间:2017-09-18 23:19:44

标签: ios swift core-data uicollectionview

当我使用核心数据保存从collectionView单元格中下载的一些图像时,我收到错误'NSInternalInconsistencyException',原因:' - [UICollectionViewController loadView]加载了“BYZ-38-t0r-view-8bC- Xf-vdC“nib但没有获得UICollectionView。” 有我的albumViewController代码:

class albumViewController: coreDataCollectionViewController, MKMapViewDelegate {

// Properties

@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var albumCollection: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()

    self.albumCollection.dataSource = self
    self.albumCollection.delegate = self
    // Show the pin
    let spanLevel: CLLocationDistance = 2000
    self.mapView.setRegion(MKCoordinateRegionMakeWithDistance(pinLocation.pinCoordinate, spanLevel, spanLevel), animated: true)
    self.mapView.addAnnotation(pinLocation.pinAnnotation)

    // Set the title
    title = "Photo Album"

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

    // Create a fetchrequest
    let fr = NSFetchRequest<NSFetchRequestResult>(entityName: "Album")
    fr.sortDescriptors = [NSSortDescriptor(key: "imageData", ascending: false),NSSortDescriptor(key: "creationDate", ascending: false)]

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

    // Put photos in core data
    let photoURLs = Constants.photosUrl
    for photoUrLString in photoURLs {
        let photoURL = URL(string: photoUrLString)
        if let photoData = try? Data(contentsOf: photoURL!) {
            let photo = Album(imageData: photoData, context: fetchedResultsController!.managedObjectContext)
        } else {
            print("Image does not exist at \(photoURL)")
        }
    }
}

// Find number of items
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 9
}


override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCollectionViewCell", for: indexPath) as! photoCollectionViewCell
    let album = fetchedResultsController!.object(at: indexPath) as! Album
    performUIUpdatesOnMain {
        cell.photoImageView?.image = UIImage(data: album.imageData as! Data)
    }
    return cell
}

// Reload photos album
@IBAction func loadNewPhotos(_ sender: AnyObject) {
}    
}

我的代码是

class coreDataCollectionViewController: UICollectionViewController {

// Mark: Properties
var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>? {

    didSet {
        // Whenever the frc changes, we execute the search and
        // reload the table
        fetchedResultsController?.delegate = self
        executeSearch()
        collectionView?.reloadData()
    }
}

// Mark: Initializers

init(fetchedResultsController fc: NSFetchedResultsController<NSFetchRequestResult>, collectionViewLayout: UICollectionViewFlowLayout) {
    fetchedResultsController = fc
    super.init(collectionViewLayout: collectionViewLayout)
}

// Do not worry about this initializer. I has to be implemented because of the way Swift interfaces with an Objective C protocol called NSArchiving. It's not relevant.
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}
}

// Mark: CoreDataTableViewController (Subclass Must Implement)
extension coreDataCollectionViewController {

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    fatalError("This method MUST be implemented by a subclass of CoreDataTableViewController")
}
}

// Mark: CoreDataTableViewController (Table Data Source)

extension coreDataCollectionViewController {

override func numberOfSections(in collectionView: UICollectionView) -> Int {
    if let fc = fetchedResultsController {
        return (fc.sections?.count)!
    } else {
        return 0
    }
}

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if let fc = fetchedResultsController {
        return fc.sections![section].numberOfObjects
    } else {
        return 0
    }
}
}

// Mark: CoreDataTableViewController (Fetches)

extension coreDataCollectionViewController {

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

}

// MARK: - CoreDataTableViewController: NSFetchedResultsControllerDelegate

extension coreDataCollectionViewController: NSFetchedResultsControllerDelegate {

func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
}
}

但如果我不使用核心数据,我就会成功。有什么不同吗?

class albumViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MKMapViewDelegate {

// Properties

@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var albumCollection: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()
    let spanLevel: CLLocationDistance = 2000
    self.mapView.setRegion(MKCoordinateRegionMakeWithDistance(location.pinCoordinate, spanLevel, spanLevel), animated: true)
    self.mapView.addAnnotation(location.pinAnnotation)
}

// Find number of items
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return Constants.photosUrl.count
}


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCollectionViewCell", for: indexPath) as! photoCollectionViewCell
    let photoURL = URL(string: Constants.photosUrl[(indexPath as NSIndexPath).item])
    if let photoData = try? Data(contentsOf: photoURL!) {
        performUIUpdatesOnMain {
            cell.photoImageView?.image = UIImage(data: photoData)
        }
    } else {
        print("Image does not exist at \(photoURL)")
    }
    return cell
}

// Reload photos album
@IBAction func loadNewPhotos(_ sender: AnyObject) {
}


} 

顺便说一句,有我的代码链接 https://github.com/MartinSnow/MyVirtualTourist.git 我的项目在“storeLocation”分支上。

1 个答案:

答案 0 :(得分:0)

除了使用CoreData之外,这两个实现之间的主要区别在于“工作”版本是UIViewController的子类:

class albumViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MKMapViewDelegate { .... }

而抛出错误的版本是UICollectionViewController的子类:

class albumViewController: coreDataCollectionViewController, MKMapViewDelegate { .... }
class coreDataCollectionViewController: UICollectionViewController { .... }

UICollectionViewControllers要求他们的根viewUICollectionView或其子类。在你的故事板中,albumVC有一个collectionView,但不是根视图。

解决方案是将coreDataCollectionViewController的类定义更改为子类UIViewController并采用集合视图委托和数据源协议:

class albumViewController: coreDataCollectionViewController, MKMapViewDelegate { .... }
class coreDataCollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { .... }

另请注意,错误实际上是由mapViewController场景抛出的,该场景也是coreDataCollectionViewController的子类,并且在故事板中没有collectionView。上述修复也适用于此。