Collection view not showing images properly - ios

In view B I have an imageview which can have an image from gallery. The image is stored in coredata as NSData
Now if I click on a submit button & return back to a view A, I have a collectionview where I want to display the image. If I again go to view B and add another image, click on submit & return to view A I should see 2 different images.
But when I return, what I’m seeing is the first image twice instead of 2 different images. But when I run the app again I see all different images
as required...
In viewWillAppear of view A this is what I’ve done…
//Fetching from Database
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Customers2")
do {
customerDetails2 = try managedContext.fetch(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) as! [Customers2]
for result in customerDetails2 {
if let imageData = result.value(forKey: "image") as? NSData {
}
}
collectionview2.reloadData()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
In numberOfItemsInSection of view A….
count1 = customerDetails2.count
In cellForRowAtIndexPath of view A…
let dfg = customerDetails2[indexPath.row]
let asd = dfg.image
if let image = UIImage(data: asd! as Data) {
imageArray.append(image)
cell1.customerImageView.image = imageArray[indexPath.row]
}

Issue is With the below line
imageArray.append(image)
As you mentioned the issue occurs after adding the second image
So you might be reloading the collection view again to show the images.
The above code will be executed again and it will append the image for the row zero at the index 1 and row 1 at index 2 so when you try to show the image for row1, it will be same as row 0 ..
Use a dictionary if you want to cache the images
like below
//Declare a poperty
var imageDict = Dictionary<String,UIImage>()
//At cellForRow
imageDict["\(indexpath.row)"] = image
also check if the image exists in cache
if let image = imageDict["\(indexpath.row)"] as? UIImage {
//Load Image
} else {
//Initialise from data
}

Related

Firebase storage image won't append to array

So I'm trying to download a set of images to display in a collectionview in my app. I have the images in the firebase storage, and their url in my database under the node "img" in each item node with a child "storage" with the url as the key.
I started by creating an empty array of UIImage: var somePicArray = [UIImage]() and then appending the pictures to this array through this funciton:
func test() {
let penis = Database.database().reference().child("kategorier").child("brus")
penis.observeSingleEvent(of: .value) { (snapshot: DataSnapshot) in
for item in snapshot.children {
let snap = item as! DataSnapshot
let imageSnap = snap.childSnapshot(forPath: "img/storage")
if let url = imageSnap.value as? String {
let someRef = self.storageRef.reference(forURL: url)
someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in
if let error = error {
print(error)
} else {
let image = UIImage(data: data!)
self.testPicArray.append(image!)
self.collectionView?.reloadData()
}
}
}
}
}
}
up until the "if let url" line everything is ok, as I have tried printing the url within this if statement, and it prints the all the urls as strings perfectly.
however I'm a little more uncertain about the seccond part. I have used this block of code elsewhere in my app to display pictures from firebase storage and this works fine. However it does not seem to work within this funciton, and I'm not sure what I'm doing wrong.
In order to display the images in the cells I tried using the following code in the collectionview cellforitematindexpath method:
cell.cellimage.image = somePickArray[2]
I just chose a random number in the array to see if i can display any picture but it just crashes saying index out of range at this line, which I assume is because the array is empty.
When the vc appears the array is empty so
cell.cellimage.image = somePickArray[2]
will crash before array is filled with data , you need to make sure you do
return somePickArray.count
inside numberOfItemsAtRow

Firebase storage images random order in collection view

In my firebase database i have several items that have a name and an image each. In my app I would like to display said parameters in a collectionview with an imageview and a label in each cell. I'm able to display each items name in the label, and I'm also able to display the items image in the imageview, however the images downloaded from firebase keep appearing in a random order every time. The labels show the item names in a correct fixed order every time. Each item has a child named "image" with a child named "storage" which stores the image url from firebase storage. The code for downloading images:
func downloadImg() {
let ref = Database.database().reference().child(itemNumber1)
ref.observeSingleEvent(of: .value) { (snapshot) in
for item in snapshot.children {
let snap = item as! DataSnapshot
let imageSnap = snap.childSnapshot(forPath: "image/storage")
if let url = imageSnap.value as? String {
let someRef = self.storageRef.reference(forURL: url)
someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in
if let error = error {
print(error)
} else {
let image = UIImage(data: data!)
self.imageArray.append(image!)
self.collectionView?.reloadData()
}
}
}
}
}
}
As I said, the code works fine, but I'd like the images to show up in the same order everytime, and the same order as they are stored in the storage/database. I tried appending the url constant to an empty array and printing it, and this array prints the storage urls in the order I want. However something seems to happen when the images themselves are appended to the imageArray.

Images showing with activityIndicator as images is being refreshed

I am working on VirtualTourist project on Udacity and we are required to display images as the images are being refreshed through a Flickr Client. Currently my implementation is such that the refresh will only show once the images are fully downloaded. The project requires such that the images are shown as an when they are downloaded, and while the downloading is happening, an activity indicator is displayed instead.
My code as follows:
#IBAction func newCollectionButtonDidTap(_ sender: Any) {
print("FETCHING NEW COLLECTION...")
DispatchQueue.main.async {
self.photos.removeAll()
self.collectionView.reloadData()
print("Reload Data newColVC")
}
for items in fetchedResultsController.fetchedObjects! {
context.delete(items as! NSManagedObject)
}
loadPhotos()
}
func loadPhotos() {
newColActivityIndicator.startAnimating()
newColActivityIndicator.isHidden = false
newCollectionButton.isUserInteractionEnabled = false
newCollectionButton.alpha = 0.4
FlickrClient.sharedInstance.getPhotos(pin.coordinate.latitude as AnyObject, lon: pin.coordinate.longitude as AnyObject, { (results, error) in
if let error = error {
print(error)
} else {
if results != nil {
for result in results! {
let picture = Photo(pin: self.pin, imageData: result as NSData, context: self.context)
self.photos.append(picture)
}
do {
try self.delegate.stack?.saveContext()
} catch let error as NSError {
print("Error saving context in loadPhotos(): \(error.localizedDescription)")
}
DispatchQueue.main.async {
self.collectionView.reloadData()
self.newColActivityIndicator.stopAnimating()
self.newColActivityIndicator.isHidden = true
self.newCollectionButton.isUserInteractionEnabled = true
self.newCollectionButton.alpha = 1.0
print("Reload Data loadPhotos")
}
}
}
})
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ImageViewCell
cell.activityIndicator.startAnimating()
cell.activityIndicator.isHidden = false
let photo = photos[indexPath.row]
if let photoImage = photo.imageData {
print("PhotoImage present")
DispatchQueue.main.async {
cell.activityIndicator.stopAnimating()
cell.activityIndicator.isHidden = true
cell.imageView?.image = UIImage(data: photoImage as Data)
}
} else {
print("PhotosImage don have")
}
return cell
}
Some advice here is much appreciated, thanks!
So firstly when you tap newCollectionButton you are removing all the data stored in core data as well as removing all those which are being displayed in collection view. So at this moment, collection view doesnot have anything to display. as a result it should be empty showing nothing.
Now, i think your loadPhotos methods returns the collection of photos from flicker which you add it to your local array and coredata. datasource of collection view must be using data from photos array.
You remove the activity indicator and then reload the collection view. At this point, you already have the necessary data for the images to be shown available. So cellForItemAt retrives data from local array, and create image and displays it. Maybe this process is happenning so fast, that your cell.activityIndicator.startAnimating() & cell.activityIndicator.stopAnimating() is executing much faster.
Since your question is unclear, & your loadPhotos fetches collection of photos data, if you want that activityIndicator to be shown in each cell, until all the image gets downloaded, you can
Remove just imagedata present in Photo object stored in local/coredata
make cell to show activity indicator if there is no image data present in each cell
reloadData() so that activity indicator starts
call loadPhotos
After downloading all photos, clear photo array, store downloaded Photos in array and call reloadData() so that all the images gets displayed.

how to update the image in table view cell using core data

I am picking image from local device gallery using image picker but the image is not getting saved and displayed in the table view cell once the gallery gets dismissed using core data using xcode 7.3 ,ios9 and swift 2.2
if isUpdate == true{
print("object id \(self.store?.objectID)")
self.store?.sName = name.text
self.store?.sDescription = desc.text
//save.setTitle("my text here", forState: .Normal)
let img = UIImage(named: "image.jpeg")
let imgData = UIImageJPEGRepresentation(img!,1)
self.store?.sImage = imgData
do {
try appdelegate.managedObjectContext.save()
self.navigationController?.popViewControllerAnimated(true)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}else{
//get the description of the entity
let storeDescription = NSEntityDescription.entityForName("Store",inManagedObjectContext: appdelegate.managedObjectContext)
//we create managed object to be inserted to core data
let store = EventsandnotesStore(entity : storeDescription!,insertIntoManagedObjectContext:appdelegate.managedObjectContext)
store.sName = name.text
store.sDescription = desc.text
//
let img = UIImage(named: "image.jpeg")
let imgData = UIImageJPEGRepresentation(img!,1)
store.sImage = imgData
do {
try appdelegate.managedObjectContext.save()
self.navigationController?.popViewControllerAnimated(true)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
}
Since you didn't mention issues with the Name and Description fields or the creation of the store object, I'm assuming that all worked and you're OK with the CoreData process.
One thing to check is the integer value 1 in the JPEG conversion call. That should be a Float so try 1.0 in the call, though I'm surprised that didn't get caught by XCode. Not sure whether that could cause your issue.
The other potential issue you may have with the conversion that I've bumped up against is that the UIImageJPEGRepresentation call can fail for other reasons, returning a nil value (e.g., this happened when I converted a CIImage directly to a UIImage, but it worked if I did it via a CGImage - go figure). Given all that, you should check the result of the call. I use a guard statement and custom logger as follows:
guard let thumbnailData = UIImageJPEGRepresentation(thumbnailUI, Set.shared.jpegQuality) else
{
logError(#file, line: #line, column: #column, function: #function, description: "JPG Conversion Error - thumbnail")
return nil
}
If that's failing you'll need to check on the images you're getting from the picker.
If you are having problems with the database side of things, have you included the TableView delegate functions in your view controller (controllerWillChangeContent, etc.). You'll need these so that your configureCell method gets called when the record with the image changes.
Those are the things I'd start with.

SDWebImage implementation in parse using Swift

I am using parse to retrieve my images and labels and display it on a collection view. The problem was that the collection view loads all the images and labels at once making the load time long and memory usage was high. I was thinking that I would load 10 cells each time however I was recommended to use SDWebImage to make the app lighter. However I don't know how to implement it with parse using swift. I am suspecting that I would put some code in this piece of code below
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("newview", forIndexPath: indexPath) as! NewCollectionViewCell
let item = self.votes[indexPath.row]
let gesture = UITapGestureRecognizer(target: self, action: Selector("onDoubleTap:"))
gesture.numberOfTapsRequired = 2
cell.addGestureRecognizer(gesture)
// Display "initial" flag image
var initialThumbnail = UIImage(named: "question")
cell.postsImageView.image = initialThumbnail
// Display the country name
if let user = item["uploader"] as? PFUser{
item.fetchIfNeeded()
cell.userName!.text = user.username
var profileImgFile = user["profilePicture"] as! PFFile
cell.profileImageView.file = profileImgFile
cell.profileImageView.loadInBackground { image, error in
if error == nil {
cell.profileImageView.image = image
}
}
var sexInt = user["sex"] as! Int
var sex: NSString!
if sexInt == 0 {
sex = "M"
}else if sexInt == 1{
sex = "F"
}
var height = user["height"] as! Int
cell.heightSexLabel.text = "\(sex) \(height)cm"
}
if let votesValue = item["votes"] as? Int
{
cell.votesLabel?.text = "\(votesValue)"
}
// Fetch final flag image - if it exists
if let value = item["imageFile"] as? PFFile {
println("Value \(value)")
cell.postsImageView.file = value
cell.postsImageView.loadInBackground({ (image: UIImage?, error: NSError?) -> Void in
if error != nil {
cell.postsImageView.image = image
}
})
}
return cell
}
I have implemented SDWebImage using Pods and have imported through the Bridging Header. Is there anyone who knows how to implement SDWebImage with parse using Swift?
You should rethink your approach -
I believe you are using collectionViewDelegate method - collectionView(_:cellForItemAtIndexPath:)
this fires every time the collection view needs a view to handle.
In there you can access the cell imageView and set its image (For Example)-
cell.imageView.sd_setImageWithURL(url, placeholderImage:placeHolderImage, completed: { (image, error, cacheType, url) -> Void in })
And if you wish to fade in the image nicely, you could -
cell.imageView.sd_setImageWithURL(url, placeholderImage:placeHolderImage, completed: { (image, error, cacheType, url) -> Void in
if (cacheType == SDImageCacheType.None && image != nil) {
imageView.alpha = 0;
UIView.animateWithDuration(0.0, animations: { () -> Void in
imageView.alpha = 1
})
} else {
imageView.alpha = 1;
}
})
EDIT
I see the you use Parse, so you don't need SDWebImage, you need to use Parse - PFImageView, It will handle your background fetch for the image when it loads. You will need to save reference to your PFObject, but I believe you already do that.
For example (inside your cellForItemAtIndexPath)-
imageView.image = [UIImage imageNamed:#"..."]; // placeholder image
imageView.file = (PFFile *)someObject[#"picture"]; // remote image
[imageView loadInBackground];
How many objects are displaying in the collection view?
Since you mentioned SDWebImage, are you downloading the images in the background as well?
If you want to load the images as the user scrolls, have a look at the documentation for SDWebImage. The first use case describes how to display images in table view cells withouth blocking the main thread. The implementation for collection view cells should be similar.

Resources