Collectionview with coredata problems - swift

The problem is when I switch between pages (TabNavigation) and I return in this page, a cell is added unwantedly, I rewrite the code the code many times, can someone help me?
CoreData is implemented it to save favorites in this collection view, and everything works except this little bug
var Distance : String!
var Logo : UIImage!
var pp : String!
var menuu : UIButton!
var loc : String!
var shop: [NSManagedObject] = []
#IBOutlet weak var ShopCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
ShopCollectionView.layer.cornerRadius = 10
ShopCollectionView.layer.masksToBounds = true
// register cell
let nibCell = UINib(nibName: ShopCollectionViewCellId, bundle: nil)
ShopCollectionView.register(nibCell, forCellWithReuseIdentifier: ShopCollectionViewCellId)
ShopCollectionView.delegate = self
ShopCollectionView.dataSource = self
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "ShopsData", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "ShopsData")
do {
let result = try? managedContext.fetch(fetch) as? [ShopsData]
shop = result ?? []
} catch {
fatalError()
}
collectionView.reloadData()
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return shop.count + 1
}
<This is my writed method>
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row >= shop.count {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ShopCollectionViewCellId, for: indexPath) as! ShopCollectionViewCell
return cell
} else {
let shop = shop[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ShopCollectionViewCellId, for: indexPath) as! ShopCollectionViewCell
cell.People.text = shop.value(forKey: "actualCustomers") as? String
cell.Location.text = shop.value(forKey: "location") as? String
return cell
}
}
This is the code I write

In your numberOfItemsInSection you return shop.count + 1 which means you are telling your collection view to show you 1 additional cell than the actual data you have.
Then in your cellForItemAt indexPath, you handle this by creating 1 blank cell.
If suggest you make the following changes to these functions as shown below and perhaps you will see the results you expect
override func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int
{
return shop.count
}
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let shop = shop[indexPath.row]
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier: ShopCollectionViewCellId,
for: indexPath) as! ShopCollectionViewCell
cell.People.text = shop.value(forKey: "actualCustomers") as? String
cell.Location.text = shop.value(forKey: "location") as? String
return cell
}

Related

swift different number of cell in each section

I'm trying to pull data from firebase and show it in a collectionView with sections. How can add a different number of cells per section as each firebase data node will not have the same number of posts.
var users = [User]()
var posts = [Post]()
Updated collection view methods:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! FollowingCell
let user = self.users[indexPath.section]
let post = self.dataSource[user]?[indexPath.row]
cell.post = post
return cell
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerId, for: indexPath) as! FollowingHeader
let user = self.users[indexPath.section]
header.user = user
return header
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let user = self.users[section]
return self.dataSource[user]?.count ?? 0
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return self.dataSource.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 120, height: 80)
}
How I'm adding the posts and user. As you can see I'm adding the post to the dataSource but for some reason, only 1 post per use is added when one of the users has two posts.
func fetchFollowedEvents() {
self.collectionView.refreshControl?.endRefreshing()
guard let currentUid = Auth.auth().currentUser?.uid else { return }
let ref = Database.database().reference().child("followed-posts").child(currentUid)
ref.observeSingleEvent(of: .value) { (snapshot) in
guard let dictionary = snapshot.value as? [String: Any] else { return }
dictionary.forEach { (key, value) in
Database.database().reference().child("posts").child(key).observeSingleEvent(of: .value) { (postSnapshot) in
guard let postDictionary = postSnapshot.value as? [String: Any] else { return }
guard let uid = postDictionary["uid"] as? String else { return }
Database.fetchUserWithUID(uid: uid) { (user) in
let post = Post(postId: key, user: user, dictionary: postDictionary)
post.id = key
self.users.append(user)
self.dataSource[user] = [post]
self.collectionView.reloadData()
}
}
}
}
}
try creating data source like this
var users : [User] = []()
var dataSource : [User:[Post]] = [:]() //I Prefer this one
(or)
var dataSource :[Int: [Post]] = [:]()
a dictionary with Users as Key and Array of post as Value
So now for number of sections you can use
self.userser.count()
(or)
self.dataSource.count()
So for number of rows in sections, you can do as so
let user = self.users[section]
return self.dataSource[user].count() ?? 0
(or)
return self.dataSource[section].count() ?? 0
in cell for row at index you can get the data as
let user = self.users[indexPath.section]
let data = self.dataSource[user][indexPath.row]
(or)
let data = self.dataSource[indexPath.section][indexPath.row]
Hope this would help you.
Happy Codding!

Adding data as many as indexPath - Swift

I draw data from the database, assign the data I have taken to LINKSTRING. The data I have assigned is the link and I am viewing the photos using this link. For example, there are 5 data I have assigned to LINKSTRING how can I assign these 5 data to photos variable. I want to add up to x photos in LINKSTRING.
Example:
self.photos = [CustomPhotoModel (imageURL: URL (string: "\ (self.LINKSTRING [0])"), thumbnailImageURL: URL (string: "\ (self.LINKSTRING [0]))"))] self.photos = [CustomPhotoModel (imageURL: URL (string: "\ (self.LINKSTRING [1])"), thumbnailImageURL: URL (string: "\ (self.LINKSTRING [1]))"))]
How do I assign variables in the form?
import UIKit
import INSPhotoGallery
class CustomModelViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
lazy var photos = [CustomPhotoModel]()
var LINKSTRING : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
fillWithYORUM()
collectionView.delegate = self
collectionView.dataSource = self
}
func fillWithYORUM() {
let client = SQLClient.sharedInstance()!
client.connect("...", username: "...", password: "...", database: "...") { success in
client.execute("SELECT ... FROM ...", completion: { (_ results: ([Any]?)) in
var gifsa: [String] = []
for table in results as! [[[String:AnyObject]]] {
for row in table {
for (_, value) in row {
if let intVal = value as? String {
gifsa.append(String(intVal)) }} }}
DispatchQueue.main.async {
self.photos = [CustomPhotoModel(imageURL: URL(string: "\(LINKSTRING)"), thumbnailImageURL: URL(string: "\(LINKSTRING)"))]
self.collectionView.reloadData()
}
client.disconnect()
}) } }
}
extension CustomModelViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ExampleCollectionViewCell", for: indexPath) as! ExampleCollectionViewCell
cell.populateWithPhoto(photos[(indexPath as NSIndexPath).row])
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return photos.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! ExampleCollectionViewCell
let currentPhoto = photos[(indexPath as NSIndexPath).row]
let galleryPreview = INSPhotosViewController(photos: photos, initialPhoto: currentPhoto, referenceView: cell)
galleryPreview.referenceViewForPhotoWhenDismissingHandler = { [weak self] photo in
if let index = self?.photos.firstIndex(where: {$0 === photo}) {
let indexPath = IndexPath(item: index, section: 0)
let cell = collectionView.cellForItem(at: indexPath) as? ExampleCollectionViewCell
return cell
}
return nil
}
present(galleryPreview, animated: true, completion: nil)
}
}
you just need to append the data to your photos array like this:
if LINKSTRING.count > 0 {
for photoUrl in LINKSTRING {
let data = CustomPhotoModel (imageURL: URL (string: photoUrl), thumbnailImageURL: URL (string: photoUrl)]
self.photos.append(data)
}
}

CollectionViewCell is no displayed receiving data from Firebase

I have to fill the cells with data from the firebase. But they are not displayed. Help and explain where I made a mistake.
How to fill cell data ?
class TrainingProgramViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
//var trainingPrograms = TrainingProgram.fetchTrainingProgram()
var trainingPrograms = [TrainingProgram]()
let cellScale: CGFloat = 0.7
override func viewDidLoad() {
super.viewDidLoad()
fetchPrograms()
}
func fetchPrograms() {
Database.database().reference().child("programs").observe(.childAdded) { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
print(dict)
let newTitle = dict["title"] as! String
print("Новый тайтл:" + newTitle)
let newDescription = dict["description"] as! String
let trainingCell = TrainingProgram(description: newDescription, title: newTitle)
self.trainingPrograms.append(trainingCell)
print(self.trainingPrograms)
}
}
}
}
extension TrainingProgramViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return trainingPrograms.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TrainingProgramCollectionViewCell", for: indexPath) as! TrainingProgramCollectionViewCell
let trainingProgram = trainingPrograms[indexPath.item]
cell.trainingPrograms = trainingProgram
return cell
}
This is a model:
class TrainingProgram
{
var description = ""
var title = ""
init(description: String, title: String) {
self.description = description
self.title = title
}
}
This is my structure of Database :
You forget to reload your collectionView
func fetchPrograms() {
Database.database().reference().child("programs").observe(.childAdded) { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
print(dict)
let newTitle = dict["title"] as! String
print("Новый тайтл:" + newTitle)
let newDescription = dict["description"] as! String
let trainingCell = TrainingProgram(description: newDescription, title: newTitle)
self.trainingPrograms.append(trainingCell)
print(self.trainingPrograms)
}
DispatchQueue.main.async {
self. collectionView.reloadData()
}
}
}
Also set
collectionView.delegate = self
collectionView.dataSource = self
In your collectionView Method
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TrainingProgramCollectionViewCell", for: indexPath) as! TrainingProgramCollectionViewCell
cell.titleLabel.text = trainingPrograms[indexPath.item].title
return cell
}
After you fetch the data and append it to your array which is shown in your tableView/collectionView you always have to reloadData() like collectionView.reloadData() or tableView.reloadData()

Empty Collection View Swift

I followed 1 tutorial and i was able to fill a collectionView with some data(imageview and text):
let appleProducts = ["A", "B", "C", "D"]
let imageArray = [UIImage(named: "pug1"), UIImage(named: "pug2"), UIImage(named: "pug3"), UIImage(named: "pug4")]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return appleProducts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath as IndexPath) as! CollectionViewCell
cell.imageView?.image = self.imageArray[indexPath.row]
cell.title?.text = self.appleProducts[indexPath.row]
return cell
}
Now passing from the demo project to mine, I want to fill this CollectionView with data(Picture and text) that I get from FirebaseDatabse so I created this method:
struct item {
let pictureId: String!
let picture: String!
}
var items = [item]()
func getLatestAddedItems(){
self.items.removeAll()
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Items").observe(.value, with: {
snapshot in
//self.items.insert(item(picture: picture), at: 0)
for childSnap in snapshot.children.allObjects {
let snap = childSnap as! FIRDataSnapshot
//print(snap.key)
let picture = (snap.value as? NSDictionary)?["bookImage"] as? String ?? ""
//print(picture)
self.items.append(item(pictureId:snap.key, picture:picture))
}
print(self.items.count)
})
}
And I create this button to call GetLatestAddedItems Method:
#IBAction func selectAction(_ sender: AnyObject) {
getLatestAddedItems()
}
And this one to check results:
#IBAction func gettableAction(_ sender: AnyObject) {
print(self.items[0].picture)
print(self.items[1].picture)
print(self.items[2].picture)
print(self.items.count) }
OutPut results:
picture 1 link
picture 2 link
picture 3 link
3
Everythings look fine and correct, now after making required changes in ContentView methods:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath as IndexPath) as! CollectionViewCell
//cell.imageView?.image = self.imageArray[indexPath.row]
let url = NSURL(string: items[indexPath.row].picture)
let data = NSData(contentsOf: url! as URL)
cell.imageView?.image = UIImage(data: data! as Data)
cell.title?.text = self.items[indexPath.row].pictureId
return cell
}
Now I'm getting an empty ContentView, the first time with button it works because I call the getLatestAddedItems() that will get and add data to the Items table, I try to call it in both ViewWillAppear or Viewdidload but nothings changes.
This is what I think the return items.count is returning 0 so nothings will appear any suggestions ?
Move your collectionView's protocol delegation initialisation to one of the ViewController lifecycle scope such as viewDidLoad() or viewWillAppear(_animated : Bool) if you are using a custom viewController(i.e embed a collectionView inside a viewController)
And reload your collectionView every time your user receives a value from its database.
override func viewDidLoad(){
super.viewDidLoad()
self.collectionView.dataSource = self
self.collectionView.delegate = self
}
func getLatestAddedItems(){
self.items.removeAll()
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Items").observe(.childAdded, with: {
snapshot in
for childSnap in snapshot.children.allObjects {
let snap = childSnap as! FIRDataSnapshot
let picture = (snap.value as? NSDictionary)?["bookImage"] as? String ?? ""
self.items.append(item(pictureId:snap.key, picture:picture))
print(self.items.count)
self.collectionView.reloadData()
}
})
}
PS:- All the calls to your firebase server are asynchronous, which takes some time to retrieve the data from your FBDB, so put print(self.items.count) should be inside the completionBlock of the firebase observing call otherwise if it is put outside it will be called even before your data has been retrieved from FBDB.

How to store image from collectionviewcell to core data

I have an entity called Task where taskImage is an attribute of binary data. I have the following
#IBOutlet weak var collectionView: UICollectionView!
var imageSelected = [UIImage]()
Then I have collection view as follows
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.imageSelected.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
as! EditCollectionViewCell
cell.imageView?.image = imageSelected[(indexPath as NSIndexPath).row]
cell.layer.borderColor = UIColor.white.cgColor
cell.layer.borderWidth = 3
return cell
}
Now I am trying to save image to core data when Save bar button item is pressed as follows
#IBAction func saveBtnPressed(_ sender: AnyObject) {
if #available(iOS 10.0, *) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let task = Task(context: context)
task.taskDesc = taskText.text!
//task.taskImage = UIImagePNGRepresentation(cell.imageView?.image)
} else {
// Fallback on earlier versions
}
I am getting error for taskImage, any help regarding storing image will be appreciated.
My code to store image in core data
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
let newContact = NSEntityDescription.insertNewObjectForEntityForName("Contact", inManagedObjectContext: context)
let imageData:NSData = UIImageJPEGRepresentation(imgViewPhoto.image!, 1)!
newContact.setValue(imageData, forKey: "imageData")
and retrieve it from coredata
let imageData:NSData = contact.valueForKey("imageData") as! NSData
imgViewPhoto.image = UIImage(data: imageData, scale: 1)