Prototype cell + image + delegate - swift

So this is what happened: I created a tableView and delegated some stuff to it from another controller. The textLabels from the cells are working fine, but whenever I add cell.imageView?.image, all cells change to that specific image.
Any ideas on how to fix it? The textLabels are static but the images are not.
Here I receive the image to the tableView:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell!
let row = indexPath.row
let titulo = arrayInfo[row]
let image = imgR
cell.textLabel!.text = titulo
cell.imageView?.image = image
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "add") {
let view = segue.destinationViewController as! EsseVaiPassar
view.delegate = self
view.delegateimg = self
}
}
and here I pass it from the controller to the other one with the tableView:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage
imagemRecebida.contentMode = .ScaleAspectFit
imagemRecebida.image = pickedImage
delegateimg?.passImage(pickedImage!)
print("IMAGE PICKED: \(pickedImage)")
dismissViewControllerAnimated(true, completion: nil)
}
func imagePickerControllerDidCancel(picker: UIImagePickerController) {
dismissViewControllerAnimated(true, completion: nil)
}

The code is working as intended. For your textview, you are asking the value arrayInfo[row]. So for each cell as the indexPath.row changes the respective element from the array is displayed in the text field.
But for your image, it is just a single image and not an array. You aren't using indexPath.row to select a particular image.
To add different images to your tableView, just create an array of image like you have created an array of String. Just implement the logic you have implemented for the textView cell.imageView?.image = image[indexPath.row]
To add image only to the first cell and leave the remaining cells blank use this
if indexPath.row == 0
{
cell.imageView?.image = image
}

In `cellForRowAtIndexPath' you have this code:
...
let image = imgR
cell.textLabel!.text = titulo
cell.imageView?.image = image
...
You are assigning imgR to the image constant, then you are assigning image to imageView?.image, so every cell will get the image stored in the variable imgR.
If you have multiple images, you need to put logic to pull the correct image for the correct row (usually an array of items).

Related

Change image of custom cell with UIImagePickerController

I’ve created a custom cell that has imageView inside. I want to change the image of that imageView with the help of UIImagePickerController. When I check this function in the simulator, it doesn’t change anything. Can’t figure out the problem.
The property of the custom cell:
let imageOfPlace: UIImageView = {
let iv = UIImageView()
return iv
}()
The cell in tableView:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: ImageOfPlaceViewCell.identifierOfImageOfPlaceCell) as! ImageOfPlaceViewCell
Functions of picker:
func chooseImagePickerController(source: UIImagePickerController.SourceType) {
if UIImagePickerController.isSourceTypeAvailable(source) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.allowsEditing = true
imagePicker.sourceType = source
present(imagePicker, animated: true)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let myCell = ImageOfPlaceViewCell()
myCell.imageOfPlace.image = info[.editedImage] as! UIImage
picker.dismiss(animated: true)
}
You're creating a new cell here:
let myCell = ImageOfPlaceViewCell()
Instead of that you need to change image of an existing cell. You need to get an existing cell object from the table. To do this you can use tableView.cellForRow, and pass there index path of your row.
I'm not sure about your table structure, but you also need to make sure that when you reload your table it won't disappear, so you can store picked image somewhere else to use next time in cellForRowAt.
var pickedImage: UIImage?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: ImageOfPlaceViewCell.identifierOfImageOfPlaceCell) as! ImageOfPlaceViewCell
cell.imageOfPlace.image = pickedImage
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
let image = info[.editedImage] as! UIImage
pickedImage = image
let myCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0))
myCell?.imageOfPlace.image = info[.editedImage] as! UIImage
picker.dismiss(animated: true)
}
p.s. Also I'm not sure what's this let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! before your if, it's probably redundant(at least in case when you return cell from that if)

Loading an Image file from Firebase Storage using SDWebImage

I am trying to load an image from my Firebase Storage to be displayed on my app view.
I have 2 classes mainly
Main "ClassView" that includes a tableView.
A custom tableViewCell - "ClassCell" (This class includes an imageView where I want to display the picture from firestore. - imageView is called classImage.
Below is the tableView Data Source Extension.
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: K.tableCellNibName, bundle: nil), forCellReuseIdentifier: K.tableCellIdentifier)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let storageRef = storage.reference()
let reference = storageRef.child(K.FStore.classImagesPath)
let placeholderImage = UIImage(named: "placeholder.png")
let imageView: UIImageView = self.imageView
let cell = tableView.dequeueReusableCell(withIdentifier: K.tableCellIdentifier, for: indexPath) as! ClassCell
//below is where I try to set the my image but it is not changing anything
cell.classImage?.sd_setImage(with: reference, placeholderImage: placeholderImage)
//This I tried to make sure that I am can access the image right and it worked just fine
// cell.classImage?.backgroundColor = .black
return cell
}
The Image is not displayed using the sd_setImage method. Any help would be highly appreciated if I have any error in my code or missing a declaration anywhere.
Below is what I am getting when I run the simulator. Instead of the image displayed in these black boxes, they are empty. Also, the imageView.image is returning nil so most probably the sd_setImage is not placing the image right.
self.imgSidebarMenuImage.sd_imageIndicator = SDWebImageActivityIndicator.whiteLarge
self.imgSidebarMenuImage.sd_setImage(with: URL(string: (person["image"] as! String)), placeholderImage: UIImage(named: "logo"))
try this
#1
let storage = Storage.storage().reference(withPath: “YOUR_PATH”)
storage.downloadURL { (url, error) in
if error != nil {
print((error?.localizedDescription)!)
return
}
print(“Download success”)
//url = your will get an image URL
self.someImageURL = url!
}
#2
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// cell identifier methods
var item = itemData[indexPath.row]
let referenceImage: StorageReference = storageRef.child("YOUR_PATH")
let url = URL(string: referenceImage!)!
cell.itemImage?.sd_setImage(with: url, placeholderImage: #imageLiteral(resourceName: "placeholder"))
}
}
return cell
}

preview page for uploaded image

I am making a multiple images upload process; I want user to preview their chose images and add some description for images. In here, I make a collection view inside a table view, I want the images show in collection view cell, but I don’t know what I should code to call the images that user picked.
This is image picker controller
#objc func btnClick() {
let picker = YPImagePicker(configuration: config)
picker.didFinishPicking { [unowned picker] items, _ in
if let photo = items.singlePhoto {
print(photo.fromCamera) // Image source (camera or library)
print(photo.image) // Final image selected by the user
print(photo.originalImage) // original image selected by the user, unfiltered
print(photo.modifiedImage) // Transformed image, can be nil
print(photo.exifMeta) // Print exif meta data of original image.
}
picker.dismiss(animated: true, completion: nil)
}
present(picker, animated: true, completion: nil)
picker.didFinishPicking { [unowned picker] items, cancelled in
for item in items {
switch item {
case .photo(let photo):
print(photo)
case .video(let video):
print(video)
}
}
picker.dismiss(animated: true, completion: nil)
}
}
performSegue(withIdentifier: "UploadTableViewController", sender: nil)
here is in the tableView controller
func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("UploadCollectionViewCell",
forIndexPath: indexPath)
cell.image?.UIImageView =
return cell
}
I think you have to assign the image to your collection view cell in cellForItemAt:indexPath. So do something like:
cell.imageView.image = items[indexPath.row]
What else is missing is that once you have picked an image you have to update the collectionView. So just reload it by calling:
collectionView.reloadData()
Hope it helps.
In CellForRowAt, it is necessary to set the corresponding Image in UIImageView of cell.
cell.image = imageData[indexPath.item]
imageData is a group of Image data that User can select.
Then reload collectionView at any time.
collectionView.reloadData()
example code.
func collectionView(collectionView: UICollectionView,
            cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
  let cell = collectionView.dequeueReusableCellWithReuseIdentifier("UploadCollectionViewCell",
forIndexPath: indexPath)
  cell.image = imageData[indexPath.item]
  // It works with either item or row. But for CollectionView, .item is preferable.
  collectionView.reloadData()
  return cell
}

iOS swift 3 how can I change types in a switch statement

I have 2 TableViewCells that upload images and the code is essentially identical . What I want to do is get that code and get it into 1 function so that I can reduce duplication . I am however having trouble casting things properly . The 2 tableview Cells are called HomeTVC and ProfileTVC and they both have an UiImageView which is named profile_Image .
Here is how I call that function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTVC", for: indexPath) as! HomeTVC
homeProfile.sharedGetImage(cellType: cell,streamsModel: streamsModel, row: indexPath.row)
}
Obviously the code above belongs to the HomeTVC tableView, now here is the function
func sharedGetImage (cellType: UITableViewCell?,streamsModel: streamModel, row: Int) {
var cell = HomeTVC()
switch cellType {
case is HomeTVC :
cell = HomeTVC()
break
case is ProfileTVC :
cell = cell as! ProfileTVC
default : break
}
if streamsModel.profile_image_string[row] != "" {
if let image = streamsModel.imageCache.object(forKey: streamsModel.profile_image_string[row] as AnyObject) as? UIImage {
cell.profile_image.image = image
}
}
}
I'm getting an error inside the switch statement on cell = cell as! ProfileTVC the error is cast from HomeTVC to ProfileTVC fails unrelated type . Which I understand, how can I get around that issue ? What I want to do is detect what type the UITableViewCell is then get a variable and change it to that type so that I can access the profile_image property .
In your sharedGetImage function, you are passing a reference to the table view cell, so use that instead of creating a new cell.
func sharedGetImage(cellType: UITableViewCell?, streamsModel: streamModel, row: Int) {
if let cell = cellType as? HomeTVC {
if streamsModel.profile_image_string[row] != "" {
if let image = streamsModel.imageCache.object(forKey: streamsModel.profile_image_string[row] as AnyObject) as? UIImage {
cell.profile_image.image = image
}
}
}
if let cell = cellType as? ProfileTVC {
if streamsModel.profile_image_string[row] != "" {
if let image = streamsModel.imageCache.object(forKey: streamsModel.profile_image_string[row] as AnyObject) as? UIImage {
cell.profile_image.image = image
}
}
}
}
Alternatively, when you have two classes that have the same property, this is a great chance to use a protocol. You could create a protocol to specify that an object has a UIImageView for a profile image.
protocol ProfileImageDisplaying {
var profileImageView: UIImageView
}
You could make both cells adopt this protocol, and then you would only need to do one check (to see if the cell is ProfileImageDisplaying) instead of two.

Async images change every time while scrolling?

So I'm creating an iOS app that lets you browse through the Unsplash wallpapers and I used UICollectionView to load the images in cells but whenever I scroll through an image, I go back the image changes into a different one.
Here's the code
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
dispatch_async(downloadQueue) {
let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
let imageData = NSData(contentsOfURL: imageURL!)
var image: UIImage?
if imageData != nil {
image = UIImage(data: imageData!)
}
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
return cell
}
EDIT: Two things going on:
collectionView.dequeueReusableCellWithReuseIdentifier reuses a cell that has already been created (if there's one available). So you're dequeueing one of your previous cells.
The URL your loading your images from generates a random image each time it is called.
Thus, when you scroll to the point where the first row of your collectionview is off screen, those cells get reused. Then when you scroll back up, the cells are recreated with a new random image from "https://unsplash.it/200/300/?random"
A way of circumventing this would be to keep an array of all your images indexed based on the cell index. Of course, if your images are very big and/or you have a really large collectionView, you may run out of memory.
Take a look at this code that I have mocked up. I have not verified that the code actually works.
//instance var to store your images
var imageArray: [UIImage]?
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
// Check if we have already loaded an image for this cell index
if let oldImage: UIImage = imageArray[indexPath.row] {
cell.imageView.image = oldImage
return cell
} else {
// remove the old image, before downloading the new one
cell.imageView.image = nil
}
let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
dispatch_async(downloadQueue) {
let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
let imageData = NSData(contentsOfURL: imageURL!)
var image: UIImage?
if imageData != nil {
image = UIImage(data: imageData!)
// Save image in array so we can access later
imageArray.insert(image, atIndex: indexPath.row)
}
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
return cell
}
#toddg solution is correct. But still it have a problem in reusing the cell.
If the cell is reused before the network call completion then it will assign the downloaded image to another cell.
So I changed the code like following.
var imageArray: [UIImage]?
let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
if let oldImage: UIImage = imageArray[indexPath.row] {
cell.imageView.image = oldImage
return cell
} else {
cell.imageView.image = nil;
downloadImage(indexPath);
}
return cell
}
func downloadImage(indexPath: NSIndexPath) {
dispatch_async(downloadQueue) {
let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
let imageData = NSData(contentsOfURL: imageURL!)
var image: UIImage?
if imageData != nil {
image = UIImage(data: imageData!)
}
let cell = self.collectionView .cellForItemAtIndexPath(indexPath) as! ImageCollectionViewCell
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = image
}
}
}
Hope this helps.
Let me explain what is going on actually.
When you scroll and go back you actually see the previously displayed cell with previously downloaded image (because of dequeueReusableCellWithReuseIdentifier:), and you will keep seeing that image until your new image will not downloaded, i.e. until execution of cell.imageView.image = image line.
So, you have to do following:
set cell.imageView.image = nil after dequeueReusableCellWithReuseIdentifier: line, like so:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
cell.imageView.image = nil;
//...
This will remove previously downloaded image from imageView until new image download.
You should use something like SDWebImage or UIImageView+AFNetworking for async image downloading with cache support, because every time that your method is called the images will be downloaded again and again instead of getting cached image, and that is waste of traffic.
Good luck!