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
}
Related
I would like to have an animation when pushing/dismissing my ViewController.
The best way to show you what I mean is to look at the N26: Animation
In my case I have a CollectionView where the user can click on a cell to get to the next ViewController which I handle with a tapCallback:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// if indexPath.item is less than data count, return a "Content" cell
if indexPath.item < dataSourceArray.count {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentCell", for: indexPath) as! ContentCell
// set cell label
cell.cellLabel.text = dataSourceArray[indexPath.item].name
// set cell image
cell.wishlistImage.image = dataSourceArray[indexPath.item].image
if cell.wishlistImage.image == images[2] || cell.wishlistImage.image == images[3] {
cell.priceLabel.textColor = .gray
cell.priceEuroLabel.textColor = .gray
cell.wishCounterLabel.textColor = .gray
cell.wünscheLabel.textColor = .gray
}
// set background color
cell.imageView.backgroundColor = dataSourceArray[indexPath.item].color
cell.wishCounterView.backgroundColor = dataSourceArray[indexPath.item].color
cell.priceView.backgroundColor = dataSourceArray[indexPath.item].color
cell.customWishlistTapCallback = {
// track selected index
self.currentWishListIDX = indexPath.item
let vc = self.storyboard?.instantiateViewController(withIdentifier: "WishlistVC") as! WishlistViewController
vc.wishList = self.dataSourceArray[self.currentWishListIDX]
vc.theTableView.tableView.reloadData()
self.present(vc, animated: true, completion: nil)
}
return cell
}
To dismiss the ViewController the user simply has to tap a button:
#objc private func dismissView(){
self.dismiss(animated: true, completion: nil)
}
This is a screenshot of my CollectionView and the ViewController I present/dismiss:
Like I said I would like to have the exact same animation as in the video (also be able to "drag-dismiss" the ViewController but that is probably another question?).
If you anything is unclear feel free to ask.
I tried searching for that but I couldn't really find anything so I am grateful for any help :)
There is a really good library that does this functionality, it’s called Hero.
If you want to make your own implementation you will need to use your own UIViewControllerTransitioningDelegate
I am facing an issue with UITableVIew, lets say I have a screen with tableView having three images in its resuablecell cell also contains some text, I want to open that image as a popup to view larger so added UITapGesture, and for opening that I am passing image reference like this,
#objc func showImage (_ sender: UITapGestureRecognizer){
print("ImageTapped")
let imagePopupVC: ImagePopupViewController = self.storyboard?.instantiateViewController(withIdentifier: "imagePopupVC") as! ImagePopupViewController
imagePopupVC.isImage = true
if popUpFeedImage != nil {
// here this feedImage is the one i am passing to other VC
imagePopupVC.image = popUpFeedImage
}
self.navigationController?.pushViewController(imagePopupVC, animated: false)
}
and here i am getting this popUpFeedImage populated, in cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: MemberTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MemberTableViewCell", for: indexPath) as! MemberTableViewCell
let urlString: URL = URL(string: postContent.contentURL)!
let tap = UITapGestureRecognizer(target: self, action: #selector(showImage(_:)))
cell.feedImageView.isUserInteractionEnabled = true
cell.feedImageView.addGestureRecognizer(tap)
cell.feedImageView.sd_setImage(with: urlString, placeholderImage: nil) { (image, error, imageCacheType, url) in
self.popUpFeedImage = image
}
}
Now where the issue has come, as this image is been populated and I tap the 1st image it opens the image which is the last image in the cell which is 3rd one, and happens for all which image is been displayed in last of screen will display, now I have no idea to hold that reference for the image i have tapped on specific Index,
I have implemented didSelectItemAt for opening another viewController, so i can not use this for my this specific purpose.
Any help would be helpful thanks.
You can get your image from tapped view inside gesture recognizer handling method:
#objc func showImage (_ sender: UITapGestureRecognizer){
print("ImageTapped")
guard let tappedView = sender.view else { return }
if let imageView = tappedView as? UIImageView {
self.popUpFeedImage = imageView.image
} else {
tappedView.subviews.forEach { subView in
if subView.isKind(of: UIImageView.self) {
self.popUpFeedImage = subView.image
}
}
}
let imagePopupVC: ImagePopupViewController = self.storyboard?.instantiateViewController(withIdentifier: "imagePopupVC") as! ImagePopupViewController
imagePopupVC.isImage = true
if popUpFeedImage != nil {
// here this feedImage is the one i am passing to other VC
imagePopupVC.image = popUpFeedImage
}
self.navigationController?.pushViewController(imagePopupVC, animated: false)
}
Or you can specify tag number to your imageViews and when you want to get image just compare tapped view id and imageView id.
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.
From what I understand, I can not connect the button in my cell to my TableViewController using an outlet connection. I can however connect the button in my cell to my TableView using an action connection. This is the root of my huge problems.
In my cell I have a textview, an imageView with overlaid button, and a send button. THIS IS THE CELL
I assign an image to the imageView using imagePicker. imagePicker must be opened from the tableViewController (it can not be opened in the cell).
var MyImage = UIImage?()
var MyName = String()
var MyStatus = String()
// This is ImgButton that overlays the imageView
#IBAction func MyImageButtonAction(sender: AnyObject) {
// Select Image
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
imagePicker.allowsEditing = false
self.presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
// The image chosen by the user is now called MyImage
MyImage = image
self.dismissViewControllerAnimated(true, completion: nil)
}
Now when the send button is tapped, I make MyImage a PFFile for Parse.com and send it to the database. I don't need to give full details here. This part works fine.
The problem is that I don't know how to properly connect the cell to the tableViewController in a way that I can transfer values from one to the other. I want to get the textView.text from the cell, to the tableViewController so that from there I can send it to database with the image. The other problem is that, although the user can select an image and send it to database, they can't get the selected image into their imageView.
This is my attempt to connect the cell to the tableViewController:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyStatusTableViewCell", forIndexPath: indexPath) as! MyStatusTableViewCell
cell.MyName.text = MyName
return cell
}
I was hoping that his code meant that MyName is now the variable that contains the .text that the user input in the textView.text in the cell. I tried sending MyName to the database but it came up empty.
How do I get the textView.text from the tableViewCell into my database?
u can set tag of each element of UITableViewCell and then u can access it in cellForRowAtIndexPath like
if let textfield = cell.viewWithTag(21) as? UITextField {
textfield.text = MyName
}
You can create IBAction instead of IBOutlet and in button action you can convert sender to position and find index path like this
#IBAction func buttonClick(sender: AnyObject) {
let btnPos: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath: NSIndexPath = self.tableView.indexPathForRowAtPoint(btnPos)!
print("indexPath->\(indexPath.row)")
}
I found the problem which is inside your UITableView cellForRowAtIndexPath method. You have to load your custom cells like that below:-
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("MyStatusTableViewCell", forIndexPath: indexPath) as! MyStatusTableViewCell
if cell == nil{
//Here you have to load your custom cells like that below
let array = NSBundle.mainBundle().loadNibNamed("MyStatusTableViewCell", owner: nil, options: nil)
cell = array[0] as? MyStatusTableViewCell
}
cell.MyName.text = "MyName" //Now it will set your textview value
return cell
}
I am fetching user's Photo Asset and trying to create a custom UICollectionViewCell in my UICollectionView where the first index is a Camera Image and other cells are the Camera Images. But when I scroll, it's like the image at the index is recreated and I notice some images in my cell get the view I added at the index[0]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
//Deque an GridViewCell
let cell: GridViewCell = (collectionView.dequeueReusableCellWithReuseIdentifier(CellReuseIdentifier, forIndexPath: indexPath) as? GridViewCell)!
if indexPath.item != 0 {
let asset = assetsFetchResults[indexPath.item - 1] as! PHAsset
cell.representedAssetIdentifier = asset.localIdentifier
imageManager.requestImageForAsset(asset, targetSize: AssetGridThumbnailSize, contentMode: PHImageContentMode.AspectFill, options: nil) { (result: UIImage?, info: [NSObject : AnyObject]?) -> Void in
//print(result)
if cell.representedAssetIdentifier.isEqualToString(asset.localIdentifier) {
if let imageResult = result {
cell.imageView.image = imageResult
}
}
}
} else {
let cameraButton = UIButton(frame: CGRectMake(0,0,80,80))
cameraButton.setTitle("Camera", forState: .Normal)
cameraButton.frame = cell.imageView.bounds
cell.imageView.addSubview(cameraButton)
}
return cell
}
Below is an image for illustration
Reusable cells are reused by the collection view when they are scrolled outside the view to prevent a lot of memory use. When a certain cell gets scrolled outside of the view the collection view will mark them as reusable. Reusable cells will be reused by the collection view instead of creating new ones. This will significantly reduce memory use. Why would you keep 1000 cells in memory if only 20 of them fit on the screen at the same time.
because cells are reused, they will still contain the content of previous cell, this means that the cell still has the cameraButton as a subview. Same story with the images, you need to manually remove them at from the cell and make sure al old content gets replaced or removed.
When you perform methods that will download and set the image to the cell after a while, the cell could still contain the image of the previous cell. You should clear the image at the beginning of the cellForRowAtIndexPath method to remove the old image/subviews.
See example below:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! GridViewCell
if indexPath.row == 0 {
// Show the title becouse it's the first cell.
cell.titleLabel.hidden = false
// Title needs to be set in storyboard
// Default image for camera roll needs to be set in storyboard
} else {
// All the other cells.
cell.titleLabel.hidden = true
// Clear old content
cell.imageView.image = nil
// I don't exactly know what all this code does behind the screen, but I assume it's a method that downloads the image and add it to the imageView when it's done.
let asset = assetsFetchResults[indexPath.item - 1] as! PHAsset
cell.representedAssetIdentifier = asset.localIdentifier
imageManager.requestImageForAsset(asset, targetSize: AssetGridThumbnailSize, contentMode: PHImageContentMode.AspectFill, options: nil) { (result: UIImage?, info: [NSObject : AnyObject]?) -> Void in
//print(result)
if cell.representedAssetIdentifier.isEqualToString(asset.localIdentifier) {
if let imageResult = result {
cell.imageView.image = imageResult
}
}
}
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
// Cell that needs to open the camera roll when tapped)
if indexPath.row == 0 {
// Do something that opens the camera roll
}
}
I didn't test this code, but it should be something similar to this.