So in my app I currently have a tableView with custom cells that have an imageView in them. Here is an image for reference to see what It look like and here is the code that defines the custom cell:
class GearComponentTableViewCell: UITableViewCell {
var delegate:Stepper?
var tableViewCellPosition: Int! = nil
// Image
#IBOutlet weak var itemImage: UIImageView!
// Name
#IBOutlet weak var itemName: UILabel!
// Weight
#IBOutlet weak var itemWeight1: UILabel!
#IBOutlet weak var itemWeight2: UILabel!
// Quanity
#IBOutlet weak var itemQuanity: UILabel!
#IBAction func stepperPressed (_ sender: UIStepper!) {
if (sender.value == 1) {
sender.value = 0
delegate?.stepperWasPressed(didIncrease: true, namePassed: itemName.text!, userindexPath: tableViewCellPosition)
} else if (sender.value == -1) {
sender.value = 0
delegate?.stepperWasPressed(didIncrease: false, namePassed: itemName.text!, userindexPath: tableViewCellPosition)
}
}
// Notes
#IBOutlet weak var itemNotes: UILabel!
}
These cells are tappable and when tapped they present an alert with options for adding an image to the cell you chose:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("User selected item: \(indexPath.row) and name is \(itemArray[indexPath.row].name)")
showPopUp(itemChosen: indexPath)
}
Here is the logic for the pop up/Alert:
func showPopUp(itemChosen: IndexPath) {
let alert = UIAlertController(title: "Add Photo To Item", message: .none, preferredStyle: .actionSheet)
// alert actions
let action1 = UIAlertAction(title: "Take Photo", style: .default) { action in
self.takePhoto()
}
let action2 = UIAlertAction(title: "Import Photo", style: .default) { action in
self.importPhoto(position: itemChosen)
}
let action3 = UIAlertAction(title: "Cancel", style: .destructive)
alert.addAction(action1)
alert.addAction(action2)
alert.addAction(action3)
present(alert, animated: true,completion: nil)
}
Ignore the takePhoto() function because it isn't being used yet, but right below that I am calling the importPhoto() function that takes in the position of the item you selected:
func importPhoto(position: IndexPath) {
print("User chose to import photo")
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.delegate = self
present(picker, animated: true) {
print("cell tapped \(position)")
self.cellChosen = position
}
}
It then sets a global variable to the item you selected (I know this isn't a good practice vs. passing the actual indexPath) that I use to pass into the imagePickerControllerDelegate function:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
dismiss(animated: true)
if let cell = tableView(gearTableView, cellForRowAt: cellChosen) as? GearComponentTableViewCell {
cell.itemImage.image = image
self.updateUI()
print("item image set for the item at position: \(cellChosen.row)")
}else {
print("did not set item image")
}
}
This is where I am running into issues now. When that delegate function gets called it successfully prints the true-case in the if/else statement and the photo isn't actually being set and I'm not receiving any errors at the time it is set. The only error I receive is "Changing the translatesAutoresizingMaskIntoConstraints property of a UICollectionViewCell that is managed by a UICollectionView is not supported, and will result in incorrect self-sizing." when I tap the right bar-button item from the first photo in my post to add a new cell to the tableView. I require some info in the form of another Alert with textfields for some info about the item you are creating before the cell is actually created to I'm not sure if that error is actually relevant to what I am currently working on.
Any ideas as to how I can set the image of the imageView in my cell? I've already tried changing the Layout of my imageView from Inferred (constraints) to Auto-resizing mask and any new cells stopped appearing...
Here is a link to my repository/the viewController I was working in.
It's because you're not updating the image on the data source of the table view. You update when you dequeue the cell, but the you call updateUI that calls reloadData().
For instance, your method imagePickerController can work like this:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
dismiss(animated: true)
let item = itemArray[cellChosen.row]
let userSubmittedItem = GearItem(itemName: item.name,
itemImage: image,
itemWeight1: item.weight1,
itemWeight2: item.weight2,
itemQuantity: item.quantity,
itemNotes: item.notes)
itemArray[cellChosen.row] = userSubmittedItem
self.updateUI()
}
Change your implementation of method imagePickerController(_ picker:didFinishPickingMediaWithInfo:) to
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
picker.dismiss(animated: true)
itemArray[cellChosen.row].image = image
gearTableView.reloadRows(at: [cellChosen], with: .none)
}
When you need to update an image of a data source model. You update the model and call reload row.
About why your code doesn't work, set a breakpoint at func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell, and you will find your imageView's image is beening reset in above method.
Related
I've created a UICollectionView in Swift with photos taken from an API : https://jsonplaceholder.typicode.com/photos
I've created a window where my images can be set to fullscreen here:
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionViewCell
let url = URL(string: "https://via.placeholder.com/600/\(posts[indexPath.row].thumbnailUrl)")
cell.myImageView.downaloadImage(from: url!)
cell.myImageView.layer.cornerRadius = 25
cell.myLabelName.text = posts[indexPath.row].title
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)
let alert = UIAlertController(title: "FullScreen", message: "Are you sure you want to see the image fullscreen?", preferredStyle: .alert)
let actionyes = UIAlertAction(title: "Yes", style: .default) { action in
cell?.frame = UIScreen.main.bounds
cell?.backgroundColor = .magenta
cell?.contentMode = .scaleAspectFit
//de schimbat imagine thumbnailURL cu url
cell?.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(self.dismissFullscreenImage))
cell?.addGestureRecognizer(tap)
self.view.addSubview((cell)!)
self.navigationController?.isNavigationBarHidden = true
self.tabBarController?.tabBar.isHidden = true
}
let actionno = UIAlertAction(title: "No", style: .default) { action in
}
alert.addAction(actionno)
alert.addAction(actionyes)
present(alert, animated: true)
}
#objc func dismissFullscreenImage(sender: UITapGestureRecognizer) {
let alert2 = UIAlertController(title: "Go Back", message: "Are you sure you want to go back?", preferredStyle: .alert)
let actionyes2 = UIAlertAction(title: "Yes", style: .default) { action in
self.navigationController?.isNavigationBarHidden = false
self.tabBarController?.tabBar.isHidden = false
sender.view?.removeFromSuperview()
}
let actionno2 = UIAlertAction(title: "No", style: .default) { action in
}
alert2.addAction(actionno2)
alert2.addAction(actionyes2)
self.present(alert2, animated: true)
}
}
I'm trying to zoom the image that's in fullscreen but I don't really know where to start. My structures are here:
import Foundation
struct Post : Codable
{
let albumId : Int
let id : Int
let title : String
let url : String
let thumbnailUrl : String
}
Also, when I exit full screen my image disappears and I don't know how to keep it there. I think the problem is from here:
sender.view?.removeFromSuperview()
Can I make the image zoom from code? Or do I need something else? I've seen a lot of people using a scrollview but my images are in a collection view cell as shown here:
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet var myImageView: UIImageView!
#IBOutlet var myLabelName: UILabel!
}
Yes, calling sender.view?.removeFromSuperview() is bad because you are removing the collection view cell from the collection view. You must not do that.
You should not be doing anything to the collection view cell when you want to show the image fullscreen. Instead, create a new UIImageView with the image from the cell's image view, and show that new image view fullscreen. When you want to dismiss it, simply remove that image view. This will leave the original collection view cell and its image view untouched and still visible after the zoom.
It would also be a nice effect if you create the new image view with the same size and position of the cell's image view and then animate the new image view to fullscreen.
You could put the new image view into a UIScrollView if you want to allow the user to zoom in on the image but that's beyond the scope of your original question.
You can use ImageScrollView in place of ImageView in your cell.
Below is the link for the same.
https://github.com/huynguyencong/ImageScrollView
I am building an iOS application for my year 12 project and it entails users being able to record information about a bill they have received and having the ability to take a photo of the bill and to save it. I have used core data for my users data to be saved to. I have currently been able to get the photo taken by the user to be able to be seen on a seperate screen when the user selects a bill. Where I am having trouble is that the YouTube video I used has only shown me how to display only 1 specific photo in position zero, as shown on line 39. I need help in getting a different image being displayed dependent on what bill the user selects. For example, if a user taps a water bill, on the viewing screen, they will see a water bill. Then if the user taps a gas bill, on the viewing screen, they will see the gas bill. Currently, what is happening is regardless of whether the user selects the gas or water bill, a water bill is displayed. I have tried to explain this the best I can, if there are any other concerns, please let me know.
Thank you for your assistance
import UIKit
import CoreData
class ViewControllerViewing: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Getting keyboard to retract
}
func fetchImage() -> [Bravo] {
var fetchingImage = [Bravo]()
let fetchRequest = NSFetchRequest<Bravo>(entityName: "Bravo")
do {
fetchingImage = try context.fetch(fetchRequest)
} catch {
print("Error while fetching the image")
}
return fetchingImage
}
// Outlets
#IBOutlet weak var imgDisplay: UIImageView!
var selectedImage: String?
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var lblBill: UILabel!
// Actions
#IBAction func btnDisplay(_ sender: Any) {
let arr = DataBaseHelper.shareInstance.getAllImages()
// Got to get the numbered one change dependent on what bill is pressed
self.imgDisplay.image = UIImage(data: arr[0].photo!) // Only position zero photo displays
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
// Screen before photo screen
Class CarViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number bills
return bills.count
}
// Editing function
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Selected Person
let bravo = self.bills[indexPath.row]
// Create alert
let alert = UIAlertController(title: "Edit Bill", message: "Edit Provider", preferredStyle: .alert)
alert.addTextField()
// Edit text feild to edit provider
let txtProvider = alert.textFields![0]
// Configure button handler
let saveButton = UIAlertAction(title: "Save", style: .default) {
(action) in
// Edit provider property
bravo.provider = txtProvider.text
// Save new data
do {
try self.context.save()
}
catch {
}
// Refetch data
self.fetchBravo()
}
// Add button
alert.addAction(saveButton)
// Show alert
self.present(alert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//ensure the cell identifier has been labelled "cell"
// let bravob = self.bills[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// Recieves information from core data
let display = self.bills[indexPath.row]
// Displays the provider in the title
cell.textLabel?.text = display.provider
// Displays the date in the subtitle
cell.detailTextLabel?.text = display.date
//How to add photo into tableview research
return cell
}
func fetchBravo() {
// Fetch data from core data to display in a tableview
do {
let request = Bravo.fetchRequest() as NSFetchRequest<Bravo>
// Set the filtering and sorting on the request This is the sorting method (For setting car filter, try to adjust here) Look at predecite https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html
let pred = NSPredicate(format: "category CONTAINS '0'")
request.predicate = pred
self.bills = try context.fetch(request)
// Sort descripter
let sort = NSSortDescriptor(key: "provider", ascending: true)
request.sortDescriptors = [sort]
DispatchQueue.main.async {
self.tblCar.reloadData()
}
}
catch {
}
}
// Swipe to delete function https://www.youtube.com/watch?v=gWurhFqTsPU&list=RDCMUC2D6eRvCeMtcF5OGHf1-trw&start_radio=1
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
// Create swipe action
let action = UIContextualAction(style: .destructive, title: "Delete") { (action, view, completionHandler) in
// Which bill to removes
let BravoRemove = self.bills[indexPath.row]
// Remove Bill
self.context.delete(BravoRemove)
// Save updated delete
do {
try self.context.save()
}
catch{
}
// Refetch new data
self.fetchBravo()
}
// Return swipe action
return UISwipeActionsConfiguration(actions: [action])
}
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var bills:[Bravo] = []
override func viewDidLoad() {
super.viewDidLoad()
tblCar.delegate = self
tblCar.dataSource = self
tblCar.reloadData()
// Do any additional setup after loading the view.
}
// Outlets
#IBOutlet weak var txtDate: UITextField!
#IBOutlet weak var txtBill: UITextField!
#IBOutlet weak var tblCar: UITableView!
#IBOutlet weak var segCategory: UISegmentedControl!
// Actions
#IBAction func btnSearch(_ sender: Any) {
// Re-Fetch data for core data
self.fetchBravo()
print(bills)
tblCar.reloadData()
}
I am making an application that allows the user to keep order at usernames and passwords. Currently, I am having a problem when the user quit the app, the data or TableViewCell is not stored or showing up. I am using an array.
I assume because the data is not getting stored after. Is CoreData or UserDefaults a simple solution for this? I want to avoid Firebase.
Can someone explain or show, how to implement CoreData/UserDefaults into the code?
I have searched a lot but I simply find it hard to understand how to apply it in my code, especially with arrays and TableViewCell. Help is deeply appreciated.
Pictures are below.
Here is my code:
MainViewController:
class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView:UITableView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return informasjoner.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "InformasjonTableViewCell") as! InformasjonTableViewCell
let informasjon:Informasjon = informasjoner[indexPath.row];
if(informasjon.image != nil){
cell.imageViewInfo?.image = informasjon.image
} else {
cell.imageViewInfo?.image = UIImage(named: informasjon.imageName!)
}
cell.epostLabel?.text = informasjon.labelEpost
cell.passordLabel?.text = informasjon.labelPassord
cell.applikasjonLabel?.text = informasjon.labelApplikasjon
return cell
}
// EDIT / UPDATE CELL
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let informasjon = informasjoner[indexPath.row]
let deleteAction = UITableViewRowAction(style: .default, title: "Delete"){
(action, indexPath) in
self.deleteAction(informasjon: informasjon, indexPath: indexPath)
}
//call delete action
deleteAction.backgroundColor = .red;
return [deleteAction]
}
private func deleteAction(informasjon: Informasjon, indexPath: IndexPath){
let alert = UIAlertController(title: "Delete",
message: "Are u sure?",
preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Yes",
style: .default){ (action) in
informasjoner.remove(at: indexPath.row)
self.tableView?.deleteRows(at: [indexPath], with: .automatic)
}
let cancelAction = UIAlertAction(title: "No",
style: .default,
handler: nil);
alert.addAction(deleteAction);
alert.addAction(cancelAction);
present(alert, animated: true);
}
}
InformasjonTableViewCell:
import UIKit
class InformasjonTableViewCell: UITableViewCell {
#IBOutlet weak var imageViewInfo: UIImageView?
#IBOutlet weak var epostLabel: UILabel?
#IBOutlet weak var passordLabel: UILabel?
#IBOutlet weak var applikasjonLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
AddInfoVC:
import UIKit
class AddInfoVC: UIViewController, UITextFieldDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var txtEpost:UITextField?
#IBOutlet weak var ImageInfo:UIImageView?
#IBOutlet weak var txtPassord:UITextField?
#IBOutlet weak var txtApplikasjon: UITextField!
var newInfo = Informasjon()
#IBAction func btnSave(sender:UIButton){
print("Press Save!")
if(newInfo.image == nil || newInfo.labelEpost?.count == 0 || newInfo.labelPassord?.count == 0 || newInfo.labelApplikasjon?.count == 0){
let alertController = UIAlertController(title: "Alert", message: "Please set", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default){
(action) in
}
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil);
} else{
informasjoner.append(newInfo);
navigationController?.popViewController(animated: true)
let mainViewController = self.navigationController?.topViewController as? MainViewController
mainViewController?.tableView?.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Tap to ImageView
let tapGestureToImageView = UITapGestureRecognizer(target: self, action: #selector(tapToImageView(sender:)))
tapGestureToImageView.numberOfTapsRequired = 1
ImageInfo?.isUserInteractionEnabled = true;
ImageInfo?.addGestureRecognizer(tapGestureToImageView);
self.txtEpost?.delegate = self;
self.txtPassord?.delegate = self;
self.txtApplikasjon?.delegate = self;
}
Models
Informasjon.swift:
import Foundation
import UIKit
class Informasjon {
var imageName: String?
var image: UIImage?
var labelEpost: String?
var labelPassord: String?
var labelApplikasjon: String?
convenience init(imageName:String, labelEpost:String, labelPassord:String,labelApplikasjon:String ) {
self.init()
self.imageName = imageName
self.labelEpost = labelEpost
self.labelPassord = labelPassord
self.labelApplikasjon = labelApplikasjon
}
}
Picture 1 -> How the Application looks from the Storyboard.
Picture 2 -> How the Application looks from Simulator.
UserDefaults usually is the simplest database to use. If you have only one array to store/retrieve then stick with it. If you have much more information go with something bigger like CoreData/Realm.
let array = []()
let defaults = UserDefaults.standard
defaults.set(array, forKey: "SavedStringArray")
Feel free to check out following answer and tutorial.
how to save and read array of array in NSUserdefaults in swift?
https://medium.com/#nimjea/userdefaults-in-swift-4-d1a278a0ec79
If it's still hard to understand I would recommend to check the Objective-C version of UserDefaults - NSUserDefaults. Even if you are not familiar with Objective-C syntax it's probably easier to understand.
Save string to the NSUserDefaults?
Now the question comes where to use these methods when working with UITableView.
Whenever you want to change or retrieve the data from the store (UserDefaults in our case).
For the instance you can change the stored array calling tableView(:cellForRowAt:) methods in the tableView(:didSelectRowAt:), or you can retrieve the data in tableView(_:cellForRowAt:)
For using Realm check the official website
https://realm.io/docs/swift/latest
You should use NSUserDefaults to store preferences only – just preferences for your app’s functionality and nothing else. Anything else like user data should be stored someplace else. Especially if said that is sensitive and you don’t want to risk your user’s data as the plist file is directly available in the apps directory.
One more disadvantage for using UserDefaults to store app data is all data will be stored as a property list file. The entire file is read in and written out as a whole, so if you use UserDefaults to store a large amount of data that only changes in parts, you will be wasting a lot of time doing unnecessary I/O.
Rather, CoreData/Realm is always a better option to persist app data.
To learn how to store in CoreData -
https://www.raywenderlich.com/173972/getting-started-with-core-data-tutorial-2
https://in.udacity.com/course/ios-persistence-and-core-data--ud325
https://medium.com/xcblog/core-data-with-swift-4-for-beginners-1fc067cca707
https://code.tutsplus.com/tutorials/core-data-and-swift-managed-objects-and-fetch-requests--cms-25068
To learn how to store in Realm -
https://realm.io/docs/tutorials/realmtasks/
https://www.appcoda.com/realm-database-swift/
https://www.raywenderlich.com/112544/realm-tutorial-getting-started
I am using Swift 3, Xcode 8.2. I have a custom Table View Cell (called MyCustomCell) in which there is an image view and a button that, when clicked, opens the camera. The user can add more of these cells. I want the user to be able to click on a button, take a picture, and have that appear in the appropriate image view for that button.
My issue is that right now it is always appearing in the first image view but not the others.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage : UIImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let scancell = tableView.cellForRow(at: NSIndexPath(row: 0, section: 0) as IndexPath) as! MyCustomCell // it's this line that is wrong
scancell.imgView.contentMode = .scaleAspectFit
scancell.imgView.image = pickedImage
scancell.cameraButton.isHidden = true
};
self.dismiss(animated: true, completion: nil)
}
I'm having hard time understanding how the imagePickerController gets called and if I can pass in the custom cell that the user clicked on.
I want to do something like this:
tableView.cellForRow(at: tableView.indexPath(for: cell))
where cell is passed into the argument somehow but I'm not sure if the signature of the imagePickerController can be modified and if so, how exactly is it called?
Any help would be greatly appreciated. Thanks!
Add a tag to the button in cellForRow method and use that tag to get the cell.
var Celltag = 0
func tableView_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell((withIdentifier: "")) as! MyCustomCell
cell.customButton.tag = indexPath.row
cell.customButton.addTarget(self, action: #selector(ButtonClickMethod), for: .touchUpInside)
return cell
}
func ButtonClickMethod (sender:UIButton) {
Celltag = sender.tag
........
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage : UIImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let scancell = tableView.cellForRow(at: NSIndexPath(row: Celltag, section: 0) as IndexPath) as! MyCustomCell
scancell.imgView.contentMode = .scaleAspectFit
scancell.imgView.image = pickedImage
scancell.cameraButton.isHidden = true
};
self.dismiss(animated: true, completion: nil)
}
Hope this helps.
In your MyCustomCell class you should have this function for imagePicker where the user can select one photo and return the photo your user chose.Then, in the tableViewController data source method with cellForRow (where you also pass the indexPath of the cell your user clicked on) you should have something like
func tableView_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(...) as! MyCustomCell
let image = cell.pickImage
cell.image = image
return cell
}
I'm trying to build a recipe app with a recipe upload function. In the PostController, there will be a tableview of all the cooking steps, each in a tableview cell. in the cell there will be a textfield of description and a UIImageView for picture upload ( the picture chosen from pickerController will be displayed in this UIImageView for later upload). I'm trying to do
imageViewInCell.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleImageUpload)))
to call thehandleImageUpload() function that generates a UIImagePickerController. But by doing this, I met two problems.
I cannot get the index.row value of the cell by the selector in UITapGestureRecognizer , with out the index I cannot assign the chosen image back to the UIImageView of the cell.
Even if I got index.row in handleImageUpload, I still need the function below to assign selected image. How would this function accept my parameter and find the corresponding imageViewInCell?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let selectedImage: UIImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
imageViewInCell.image = selectedImage
}
dismiss(animated: true, completion: nil)
}
you can set the indexPath.row as the tag of your imageview in cellForRowAtIndexPath like Below
cell.yourImageView.tag = indexPath.row
and then you can get this indepath backagain using below
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
let cell = tableView.cellForRowAtIndexPath(indexPath) as! yourcellClass!
cell.yourImgeView.image = selectedImage
I'm assuming you want thehandleImageUpload() to be called only on tap on imageView instead of whole cell, hence you're using tapGesture.
Now to answer your question, assign tag to your imageView like this :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
//cell configuration
cell.imageView.tag = indexPath.row
return cell
}
And you can assign selected image to selected cell like this:
Now your handleImageUpload() is:
func handleImageUpload(_ sender: UITapGestureRecognizer){
selectedIndexPath = sender.tag
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let selectedImage: UIImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let cell = self.tableView.cellForRowAtIndexPath(selectedIndexPath) as UITableViewCell
cell.imageViewInCell.image = selectedImage
}
dismiss(animated: true, completion: nil)
}
I'm pretty late to the party, but the code below should work.
I'd have the UITableViewCell do the work of dealing with the image. Make it a UIPickerControllerDelegate and have it hold onto a closure to present the picker when necessary (as it can't present something itself as it is not a view controller).
Something like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let photoCell = tableView.dequeueReusableCell(withIdentifier: "PhotosTableViewCell", for: indexPath) as! PhotosTableViewCell
photoCell.takePicture = {[weak self] in
let imagePicker = UIImagePickerController()
imagePicker.delegate = photoCell
imagePicker.sourceType = .camera
self?.present(imagePicker, animated: true, completion: nil)
}
Have your UITableviewCell implement:
var takePicture: (() -> Void)?
to hold the closure. Then call it from a button or something to make it trigger.
Use:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
to deal with the image.