Index Path not placing image after being called

147 Views Asked by At

My swifts code goal is to place a image in the imageview pic in class customtv when the user imports a photo from the photo gallery. The code works if you hit the import button twice but on the first time the code does not work and nothing happens you can see me testing the code in the gif below. I tried to start the index at 1 but that is not working. I think imagePickerController is what is causing the problem.

enter image description here

import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
    
     var selectedIndexPath = IndexPath(row: 0, section: 0)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 4
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 118
    }
    @objc func importPhoto(_ button: UIButton) {
           selectedIndexPath = IndexPath(row: button.tag , section: 0)
           let imagePicker = UIImagePickerController()
           imagePicker.delegate = self
           imagePicker.sourceType = .photoLibrary
           present(imagePicker, animated: true, completion: nil)
       }
       
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        guard let selectedImage = info[.originalImage] as? UIImage else {
            fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
        }
        guard let cell = tableview.cellForRow(at: selectedIndexPath) as? customtv else { return }
        cell.pic.image = selectedImage
        
        tableview.reloadRows(at: [selectedIndexPath], with: .automatic)
        dismiss(animated: true, completion: nil)
    }
       
       @objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
           if let error = error {
               // we got back an error!
               let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
               ac.addAction(UIAlertAction(title: "OK", style: .default))
               present(ac, animated: true)
           } else {
               let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .alert)
               ac.addAction(UIAlertAction(title: "OK", style: .default))
               present(ac, animated: true)
           }
       }
      
    
    var tableview = UITableView()
    
     
      


      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! customtv
            cell.btn.tag = indexPath.row
            cell.btn.addTarget(self, action: #selector(importPhoto), for: .touchDown)
    
            
         
            return cell
        }
    
      


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        tableview.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
        view.addSubview(tableview)
        tableview.register(customtv.self, forCellReuseIdentifier: "cell")
        tableview.delegate = self
        tableview.dataSource = self
        
        
    }
    


}


class customtv: UITableViewCell {
    lazy var backView : UIView = {
        let view = UIView(frame: CGRect(x: 10, y: 6, width: self.frame.width  , height: 110))
        view.backgroundColor = .green
        print(self.frame.width)
        return view
    }()
    
    
    
    override func layoutSubviews() {

        backView.frame =  CGRect(x: 0, y: 6, width: bounds.maxX  , height: 110)
        
        
    }

    lazy var btn : UIButton = {
        let press = UIButton(frame: CGRect(x: 180, y: 40, width: 120 , height: 50))
        press.backgroundColor = .blue
        press.setTitle("Import", for: .normal)
        
        
        return press
    }()
    
    lazy var pic : UIImageView = {
        let press = UIImageView(frame: CGRect(x: 20, y: 40, width: 120 , height: 50))
        press.backgroundColor = .red
   
        
        
        return press
    }()
  
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(animated, animated: true)
        addSubview(backView)
        backView.addSubview(pic)
        backView.addSubview(btn)
    }
    
    
    
}
2

There are 2 best solutions below

4
On

You are using the table view in a wrong manner. The table view is using the UITableViewDataSource methods to retrieve the data by calling tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath). In that function - and only there - you fill the cell with the appropriate data. When you set the image in imagePickerController(_:, didFinishPickingMediaWithInfo:), what happens is that the next time the user scrolls or the table view decides that it needs to re-display the cell, it calls cellForRowAt which then will reset the image that you just set.

Therefore, you need to keep references to the images that the user selects in a property

class ViewController {
    var selectedImages = [IndexPath:UIImage]()
    var selectedRow:Int? 

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        // ...

        // set the image if there is one, otherwise set to nil
        cell.imageView?.image = selectedImages[indexPath]
    }
    
    @objc func importPhoto(_ button: UIButton) {
        selectedRow = button.tag
        // ...
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        guard let selectedImage = info[.originalImage] as? UIImage else {
            fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
        }
        guard let selectedRow = self.selectedRow else {
            fatalError("selectedRow is nil, which is unexpected")
        }
        let selectedIndexPath = IndexPath(row:selectedRow, section:0)
        selectedImages[selectedIndexPath] = selectedImage
        
        tableview.reloadRows(at: [selectedIndexPath], with: .automatic)
        dismiss(animated: true, completion: nil)
    }
    
}

Instead of storing the (binary image) data in selectedImages, you could also just store a reference to the camera roll. See Storing a reference to a camera roll image - Swift 4 for details

1
On

Remove the below line

tableview.reloadRows(at: [selectedIndexPath], with: .automatic)

from the method func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {