This code right here display a array on a collection view. It works exactly how I want it to.
import UIKit
import CoreData
class collectionVIEW: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var sam = ["3","j"]
var users = [User]()
#IBOutlet var theIssues: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return sam.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! whyCollectionViewCell
// cell.general.text = users[indexPath.row].userName
cell.general.text = sam[indexPath.row]
return cell
}
}
In my 2nd code. The code is slightly alerted to call core data and display on the cells. As you can see in the photo there are no cells visible. All I want to do is have the core data be displayed exactly how I was able to do in the first code set.
import UIKit
import CoreData
class collectionVIEW: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var sam = ["3","j"]
var users = [User]()
#IBOutlet var theIssues: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return users.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! whyCollectionViewCell
cell.general.text = users[indexPath.row].userName
// cell.general.text = sam[indexPath.row]
return cell
}
}
CORE DATA
import UIKit
import CoreData
class cdHandler: NSObject {
private class func getContext() -> NSManagedObjectContext {
let appdeleagetzz = UIApplication.shared.delegate as! AppDelegate
return appdeleagetzz.persistentContainer.viewContext
}
class func saveObject(userName: String) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(userName, forKey: "userName")
do {
try context.save()
return true
} catch {
return false
}
}
class func fetchObject() -> [User]? {
let context = getContext()
var users: [User]? = nil
do {
users = try context.fetch(User.fetchRequest())
return users
} catch {
return users
}
}
}
First of all do not return an optional in fetchObject(), return an empty array on failure:
class func fetchObject() -> [User] {
do {
let context = getContext()
return try context.fetch(User.fetchRequest())
} catch {
return [User]()
}
}
Second of all, to answer the question, you have to call fetchObject() in viewDidLoad to load the data
func viewDidLoad() {
super.viewDidLoad()
users = cdHandler.fetchObject()
theIssues.reloadData()
}
Note: Please conform to the naming convention that class names start with a capital letter.
Related
I am facing a problem in my application. When I run my application in the emulator, I don't get any data and just a white screen.
I decided to use Firestore as a backend. Below I provide the code and hope you can help me.
ViewController
class ViewController: UIViewController {
#IBOutlet weak var cv: UICollectionView!
var channel = [Channel]()
override func viewDidLoad() {
super.viewDidLoad()
self.cv.delegate = self
self.cv.dataSource = self
let db = Firestore.firestore()
db.collection("content").getDocuments() {( quarySnapshot, err) in
if let err = err {
print("error")
} else {
for document in quarySnapshot!.documents {
if let name = document.data()["title"] as? Channel {
self.channel.append(name)
}
}
self.cv.reloadData()
}
}
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return channel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ContentCell
let indexChannel = channel[indexPath.row]
cell.setup(channel: indexChannel)
return cell
}
}
This is my cell
class ContentCell: UICollectionViewCell {
#IBOutlet weak var channelText: UILabel!
#IBOutlet weak var subtitle: UITextView!
func setup(channel: Channel) {
channelText.text = channel.title
subtitle.text = channel.subtitle
}
}
If you need additional information - write
Your problem is here
if let name = document.data()["title"] as? Channel {
self.channel.append(name)
}
you can't cast a String to a Custom data type (Channel) , so according to your data you can try this
if let title = document.data()["title"] as? String , let subtitle = document.data()["subtitle"] as? String {
let res = Channel(title:title,subtitle:subtitle)
self.channel.append(res)
}
I have a problem. I'm making an app that consumes an API from themoviedb and saves the movies in the Realm. On the main screen I am using the CollectionViewController, and when I touch the movie it will go to details. However when it goes to details nothing appears and when I select another film it brings the previous film.
Home:
import UIKit
import RealmSwift
class HomeViewController: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UISearchBarDelegate {
#IBOutlet weak var searchbar: UISearchBar!
#IBOutlet weak var ListOfMovies: UICollectionView!
var movies : Results<Moviess>!
let realm = try! Realm()
var detailsMovies = Moviess()
override func viewDidLoad() {
super.viewDidLoad()
print(Realm.Configuration.defaultConfiguration.fileURL!)
ListOfMovies.delegate = self
ListOfMovies.dataSource = self
searchbar.delegate = self
movies = realm.objects(Moviess.self)
getRequest()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func getObjects(filter: String) -> Results<Moviess>{
movies = realm.objects(Moviess.self).filter("title CONTAINS[cd] %#",filter)
return movies
}
func getRequest(){
print(movies.count)
if movies.count < 1 {
RequestData.requisicao { (result) in
switch(result){
case .success(let detalhe):
self.ListOfMovies.reloadData()
print(detalhe)
case .failure(let error):
print(error)
}
}
}}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return movies.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listOfMovies", for: indexPath) as! HomeCollectionViewCell
let infos = movies[indexPath.item]
cell.configurationMovie(movie: infos)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
detailsMovies = movies[indexPath.row]
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return UIDevice.current.userInterfaceIdiom == .phone ? CGSize(width: collectionView.bounds.width/2-20, height: 200) : CGSize(width: collectionView.bounds.width/3-20, height: 250)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
if !searchText.isEmpty{
let filtro = getObjects(filter: searchText)
print(filtro)
ListOfMovies.reloadData()
}else{
movies = realm.objects(Moviess.self)
ListOfMovies.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "details")
{
let viewController = segue.destination as! DetailViewController
print(detailsMovies)
viewController.conta = detailsMovies
}
}
}
Details:
import UIKit
import RealmSwift
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
var conta: Moviess!
let realm = try! Realm()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return [conta].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "details", for: indexPath) as! DetailsTableViewCell
let infos = [conta][indexPath.item]
cell.prepare(movie: infos!)
return cell
}
}
Moviess
import RealmSwift
import UIKit
class Moviess: Object{
#objc dynamic var id = 0
#objc dynamic var title = ""
#objc dynamic var overview = ""
#objc dynamic var poster = ""
#objc dynamic var isFavorites = false
override class func primaryKey() -> String? {
return "id"
}
convenience init (id: Int){
self.init()
self.id = id
}
override class func indexedProperties() -> [String] {
return ["isFavorites"]
}
func insertMovieData(list: Moviess){
do {
let realm = try! Realm()
try! realm.write({ () -> Void in
realm.add(list)
})
} catch let error as NSError{
print("insert error : \(error)")
}
}
func togleFavorite(){
try? realm?.write{
isFavorites = !isFavorites
}
}
}
I have no idea where I might be giving this "bug". If someone can help and explain what is going on it will be very useful.
Your problem is that prepare(for:sender:) is called before collectionView(_:, didSelectItemAt:). prepare(for:sender:) should not rely on collectionView(_:, didSelectItemAt:).
Your prepare(for:sender:) method should look like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UICollectionViewCell,
let indexPath = ListOfMovies.indexPath(for: cell) else { return }
if (segue.identifier == "details") {
let viewController = segue.destination as! DetailViewController
viewController.conta = movies[indexPath.item]
}
}
I'm having an issue trying to get my collection view to filter data in the tableview for the category(s) set in the collection view. My goal is to have a category selected in the collection view presents the search results for the selected category in tableview shown from the category shown in the categoryLabel:
the tableview is already connected to the search bar and presents the search results accurately. But I want it to do the same for the selections/category(s) in the collection view to filter out the result selected to present the search results for that specific category in the collection view.
My data is stored in the Cloud Firestore
import Foundation
import UIKit
class Category {
var categoryLabel: String
init(categoryLabel: String) {
self.categoryLabel = categoryLabel
}
class func createCategoryArray() -> [Category] {
var categorys: [Category] = []
let category1 = Category(categoryLabel: "All")
let category2 = Category(categoryLabel: "Flower")
let category3 = Category(categoryLabel: "CBD")
let category4 = Category(categoryLabel: "Pre-Roll")
let category5 = Category(categoryLabel: "Pens")
let category6 = Category(categoryLabel: "Cartridges")
let category7 = Category(categoryLabel: "Concentrate")
let category8 = Category(categoryLabel: "Edible")
let category9 = Category(categoryLabel: "Drinks")
let category10 = Category(categoryLabel: "Tinctures")
let category11 = Category(categoryLabel: "Topical")
let category12 = Category(categoryLabel: "Gear")
categorys.append(category1)
categorys.append(category2)
categorys.append(category3)
categorys.append(category4)
categorys.append(category5)
categorys.append(category6)
categorys.append(category7)
categorys.append(category8)
categorys.append(category9)
categorys.append(category10)
categorys.append(category11)
categorys.append(category12)
return categorys
}
}
import UIKit
class CategoryScrollCell: UICollectionViewCell {
#IBOutlet weak var categoryScroll: UILabel!
#IBOutlet weak var view: UIView!
func setCategory(category: Category) {
categoryScroll.text = category.categoryLabel
}
}
import UIKit
import Firebase
class ProductListController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var productListCollectionView: UICollectionView!
#IBOutlet weak var productListTableView: UITableView!
var categorys: [Category] = []
var searchActive : Bool = false
var productInventory: [ProductList] = []
var productSetup: [ProductList] = []
override func viewDidLoad() {
super.viewDidLoad()
categorys = Category.createCategoryArray()
productListCollectionView.dataSource = self
productListCollectionView.delegate = self
productListTableView.dataSource = self
productListTableView.delegate = self
searchBar.delegate = self
fetchProducts { (products) in
self.productSetup = products
self.productListTableView.reloadData()
}
}
func fetchProducts(_ completion: #escaping ([ProductList]) -> Void) {
let ref = Firestore.firestore().collection("products")
ref.addSnapshotListener { (snapshot, error) in
guard error == nil, let snapshot = snapshot, !snapshot.isEmpty else {
return
}
completion(snapshot.documents.compactMap( {ProductList(dictionary: $0.data())} ))
}
}
}
extension ProductListController: UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productSetup.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProductListCell") as?
ProductListCell else { return UITableViewCell() }
cell.configure(withProduct: productSetup[indexPath.row])
return cell
}
}
extension ProductListController: UICollectionViewDelegate, UICollectionViewDataSource{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return categorys.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoryScrollCell", for: indexPath) as! CategoryScrollCell
let category = categorys[indexPath.row]
cell.setCategory(category: category)
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("selected")
self.productSetup.category = self.productInventory[indexPath.row]
}
}
As I understand your question,
First, here is my suggestion:
Don't use the UITableView and UICollectionView. No need to use two UI scroll classes together. You can achieve it using only UICollectionView.
Simply make a collectionView cell for category filter listing and returning it on the 0th index.
Now come to your problem, simply you can use the number of sections in CollectionView to show products with each filtered category.
I need to present a new ViewController when selecting a UICollectionView Cell and pass the data from the entity used to fill selected cell.
Here is the code used to fill cell data:
let pets = PersistenceManager.shared.fetch(Pet.self)
var _fetchResultsController: NSFetchedResultsController <Pet>?
var fetchResultsController: NSFetchedResultsController <Pet>?{
get{
if _fetchResultsController == nil {
let moc = PersistenceManager.shared.context
moc.performAndWait {
let fetchRequest = PersistenceManager.shared.petsFetchRequest()
_fetchResultsController = NSFetchedResultsController.init(fetchRequest: fetchRequest, managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil) as? NSFetchedResultsController<Pet>
_fetchResultsController?.delegate = self
do {
try self._fetchResultsController?.performFetch()
}catch {
}
}
}
return _fetchResultsController
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionViewHorizontal.dequeueReusableCell(withReuseIdentifier: "HorCell", for: indexPath) as! PRMainHorizontalCollectionViewCell
if let pet= self.fetchResultsController?.fetchedObjects, indexPath.row < pet.count{
let _pet= fetchResultsController!.object(at: indexPath)
// cell UI goes here
}
return cell
}
I understand I need to use didSelectItemAt, I just don't know what information needs to go in the function. Please let me know of anything else needed to better help answer this question. Thank you.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Added the line below based on karthik's answer. But I am unsure how to implement it.
let selectedObj = fetchResultsController!.object(at: indexPath)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "selectedPetViewController") as! PRSelectedPetViewController
navigationController?.pushViewController(vc, animated: true)
}
I prefer the following architecture:
This is the main controller with data.
For a better understanding, I will simplify the data source.
class ViewController: UIViewController {
// code ...
#IBOutlet var collectionView: UICollectionView!
fileprivate var data = [Pet]()
}
extension ViewController: UICollectionViewDataSource {
// code ...
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HorCell", for: indexPath) as? PRMainHorizontalCollectionViewCell else {
return UICollectionViewCell()
}
let pet = data[indexPath.row]
// TODO: configure cell using pet ...
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let row = indexPath.row
let pet = data[row]
// TODO: Get the child controller in any way convenient for you.
let childController = ChildViewController()
// With the help of the delegate, we will receive notification of changes pet.
childController.delegate = self
// Thus, we pass the data to the child controller.
childController.pet = pet
childController.indexPath = indexPath
// TODO: Present the view controller in any way convinient for you.
}
}
extension ViewController: ChildViewControllerDelegate {
func saveButtonPressed(_ controller: ChildViewController) {
guard let pet = controller.pet, let indexPath = controller.indexPath else {
return
}
// We save data and reload the cell whose data we changed.
self.data[indexPath.row] = pet
collectionView.reloadItems(at: [indexPath])
}
func cancelButtonPressed(_ controller: ChildViewController) {
// Do something if necessary...
}
}
In addition to the controller, the child controller also provides a delegate protocol for notification of changes.
protocol ChildViewControllerDelegate {
func saveButtonPressed(_ controller: ChildViewController)
func cancelButtonPressed(_ controller: ChildViewController)
}
// This is the controller you want to show after selecting a cell.
// I assume that in the child controller there is a button to save and cancel.
class ChildViewController: UIViewController {
var delegate: ChildViewControllerDelegate?
// The object whose data we are editing in the controller.
var pet: Pet!
// The location of the object in the main controller.
var indexPath: IndexPath!
override func viewDidLoad() {
// TODO: Configure user interface using self.pet
}
#IBAction func saveButtonPressed(_ button: UIButton) {
delegate?.saveButtonPressed(self)
}
#IBAction func cancelButtonPressed(_ button: UIButton) {
delegate?.cancelButtonPressed(self)
}
}
you can follow this to pass information to another view controller.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedObj = fetchResultsController!.object(at: indexPath)
// instantiate presenting view controller object
// add one property (manange object) in your presenting viewcontroller
// assign the selected object to that property
// present the view controller
}
When I use core data to save some images which are downloaded from internet in collectionView cell, I got an error ‘NSInternalInconsistencyException’, reason: ‘-[UICollectionViewController loadView] loaded the “BYZ-38-t0r-view-8bC-Xf-vdC” nib but didn’t get a UICollectionView.’
There is my code of 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) {
}
}
There is my code of
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>) {
}
}
But if I don't use core data, I succeed. Is there any difference?
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) {
}
}
By the way, there is my code link
https://github.com/MartinSnow/MyVirtualTourist.git
and my project is on the "storeLocation" branch.
The key difference between those two implementations, apart from the use of CoreData, is that the "working" version is a subclass of UIViewController:
class albumViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MKMapViewDelegate { .... }
whereas the version throwing the error is a subclass of UICollectionViewController:
class albumViewController: coreDataCollectionViewController, MKMapViewDelegate { .... }
class coreDataCollectionViewController: UICollectionViewController { .... }
UICollectionViewControllers require that their root view is a UICollectionView, or a subclass thereof. In your storyboard, the albumVC has a collectionView, but is not the root view.
The solution is to change the class definition for coreDataCollectionViewController to subclass UIViewController and adopt the collection view delegate and datasource protocols:
class albumViewController: coreDataCollectionViewController, MKMapViewDelegate { .... }
class coreDataCollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { .... }
Note also that the error is actually thrown by your mapViewController scene, which is also a subclass of coreDataCollectionViewController and has no collectionView in the storyboard. The above fix should work for this too.