didSelectItemAt indexPath called after a long press - ios

I have used collectionview and set the imageview in collection view cell. i need to move another viewcontroller after the cell was touched. But my problem is the view moves after a long press on cell in collection view.
This is my code.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
let imagesPath = documentPath.appendingPathComponent(reportImageFolder)
let cell = arrImagesFiltered[indexPath.item]
let imageNameWithPath = "\(imagesPath)/\(cell)"
let imageEditViewController = self.storyboard?.instantiateViewController(withIdentifier: "EditImageViewController") as! EditImageViewController
imageEditViewController.imagePath = imageNameWithPath
self.navigationController?.pushViewController(imageEditViewController, animated: true)
}

For future readers of this old question - probably me in the future ;)
I had a UITapGestureRecognizer which was swallowing my short taps and hence only the old taps were making it through! You just need to add tapRecognizer.cancelsTouchesInView = false!
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapDidTouch(sender:)))
tapRecognizer.cancelsTouchesInView = false
scrollView.addGestureRecognizer(tapRecognizer)

Related

UITapGestureRecognizer crashing app when clicking on collection view cell

I'm trying to allow user interaction in my collection view. I have decided to try to implement UITapGestureRecognizer to do this. I have tried adding a UITapGestureRecognizer to the collectionview itself and to the collectionview cell. Both ways crash the app. Here is how I am adding the UITapGestureRecognizer to the cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "userCell", for: indexPath) as! UserCell
cell.userImage.sd_setImage(with: URL(string: self.user[indexPath.row].imagePath))
cell.nameLabel.text = self.user[indexPath.row].username
cell.userID = self.user[indexPath.row].userID
let singleTap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "segueToProfile:")
singleTap.numberOfTapsRequired = 1
singleTap.numberOfTouchesRequired = 1
cell.addGestureRecognizer(singleTap)
return cell
}
When I tap on the cell I get a SIGABRT in the AppDelegate. The error message reads "terminating with uncaught exception of type NSException". What am I doing wrong. UITapGestureRecognizer.
This is my segueToProfile function:
func segueToProfile(gesture: UITapGestureRecognizer) {
// if(recognizer.state == UIGestureRecognizer.State.ended){
// print("myUIImageView has been tapped by the user.")
// }
print("hell world")
}
If you use didSelectItemAt method of collectionView your codebase look readable and maintainable.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
currentViewController.performSegue(withIdentifier: "YourSegueName", sender: nil)
}
First, I would get rid of tapgesturerecognizer as didSelectItem should handle what you are trying to accomplish. That being said, in order for this to work you must:
Remove tapgesturerecognizer
Ensure that the collection view delegate is set to self.
e.g. <yourColletionViewName>.delegate = self
Above can be assigned at viewDidLoad()

Swift - present/dismiss ViewController with UIImage animation

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

CollectionView cell is selected when swiped on

I have a collectionView showing cells. When I drag/touch/slide my finger on an item, if the touch ends on the item, the cell is selected (segues to the details screen).
Is there any way to limit cell selection (didSelectItemAt indexPath) to a simple tap? i.e it shouldn't select the cell if finger is dragged on an item and the touch ends on it.
Is this the default behavior?
I feel like it might be the cause of a cryptic issue with my custom navigation.
Thanks
Do add Following in your cellForItem
let tap = UITapGestureRecognizer(target: self, action: #selector(cellTapped(tapGestureRecognizer:)))
tap.numberOfTapsRequired = 1
cell.addGestureRecognizer(tap)
And add following function
#IBAction func cellTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
//Do your required work.
}
You can use UITapGestureRecognizer, cause it will only respond on Tap gesture:
#objc func tapAction(_ sender: UITapGestureRecognizer) {
// TODO: - Action you need
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: <CellReuseId>, for: indexPath)
let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction(_:)))
cell.contentView.addGestureRecognizer(tap)
return cell
}
But in this way didSelectItemAt will not work.

How to select and delete messages like whatsapp or iMessage on ios using swift

Hello I am trying build a simple chat application where users can send messages and photos. I am having a hard time in figuring out the best way to select and delete multiple messages on long press on a single message.
I have used collection view to display the page. Right now I am using collection view didSelect method to click on the side of chat bubble image view and able to get select button for that particular cell. But, I am not able append checkbox button for every message. I also cannot long press on the chat bubble image view.
I also tried imageview tap on chat bubble but with this I need to reload the collection view. Is there a best way of implementing delete multiple messages?
Any help is appreciated
Thanks
Below is the sample code
code for changing the checkbox image of particular cell.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
inputTextField.endEditing(true)
let cell: ChatLogMessageCell? = collectionView.cellForItem(at: indexPath) as! ChatLogMessageCell?
cell?.checkbox.isHidden = false
selectAll = true
if cell?.isSelected == true{
cell?.checkbox.image = UIImage(named: "checkedimage")
}else{
cell?.checkbox.image = UIImage(named: "uncheckedimage")
}
code to tap on chat bubble to append checkbox button to all cells.
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: chatcellId, for: indexPath) as! ChatLogMessageCell
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.imageTapped))
cell.bubbleImageView.addGestureRecognizer(tapGesture)
cell.bubbleImageView.isUserInteractionEnabled = true
if selectAll == true{
cell.checkbox.isHidden = false
}else{
cell.checkbox.isHidden = true
}}
When chat bubble is tapped collection view is reloaded to append the checkbox button to all cells
func imageTapped(){
selectAll = true
self.collectionView?.reloadData()
}
What I am finally trying to do is select and delete messages like whatsapp or iMessage (Above code is close to iMessage functionality) does. So I am completely open for complete code changes too. Thanks.
updated Code
override func viewDidLoad()
{
super.viewDidLoad()
let lpgr = UILongPressGestureRecognizer(target: self,
action: #selector(handleLongPress))
lpgr.minimumPressDuration = 0.5
lpgr.delaysTouchesBegan = true
lpgr.delegate = self
self.collectionView?.addGestureRecognizer(lpgr)
}
func handleLongPress(gestureReconizer: UILongPressGestureRecognizer) {
let p = gestureReconizer.location(in: self.collectionView)
let indexPath = self.collectionView?.indexPathForItem(at: p)
if let index = indexPath {
let cell: ChatLogMessageCell? = collectionView?.cellForItem(at: index) as! ChatLogMessageCell?
self.collectionView?.allowsMultipleSelection = true
for cell in collectionView!.visibleCells as! [ChatLogMessageCell] {
let indexPath = collectionView?.indexPath(for: cell as ChatLogMessageCell)
cell.checkbutton.isHidden = false
if selectedMsgs.contains((messages?[((indexPath)?.item)!])!) {
cell?.checkbox.image = UIImage(named: "checkedimage")
}
else {
cell?.checkbox.image = UIImage(named: "uncheckedimage")
}
}
} else {
print("Could not find index path")
}
}
On long press check boxes appear on all visible cells, but tap on chat bubble is not working.
You should attach a UILongPressGestureRecognizer to each cell in the collectionview, and set the UICollectionviewcontroller as the target for each of these recognizers. Then, when any one of them fires, set a custom property of your CollectionViewController (maybe name it editing or something) to true. Then fetch all the visible cells with the UICollectionView's visibleCells function.
In your UICollectionViewCell subclass, you should have some custom property getter/setter methods (maybe -editing and -setEditing:(BOOL)) which you can call now as you iterate through the cells in visibleCells. Within your -setEditing:(BOOL) function, you can add and remove the checkbox UIButton as you please. You'll also want to set the UICollectionView controller as the target of this UIButton, and within the UICollectionViewController, keep track of which cells are selected so when the user hits the "Delete" button, you know which messages to delete.
I would also recommend checking out https://github.com/jessesquires/JSQMessagesViewController/, which does all this logic for you.

Add a tap event to CollectionViewCell while passing Cell data

I want to add a tap event to my CollectionViewCell and to pass there my cell with the data it has. How can I achieve this?
Should this event be handled by my ViewController or by CollectionViewCell?
My ViewController:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
cell.imgImage.image = imageArray[indexPath.row]
cell.url = "xhini"
return cell
}
My CollectionViewCell:
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imgImage: UIImageView!
var url: String = "url"
}
Implement UICollectionViewDelegate and then you can use following method in the ViewController to react to selecting a cell:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let image = imageArray[indexPath.row]
// do stuff with image, or with other data that you need
}
Don't forget to set the delegate where you set a data source:
collectionView.dataSource = self
// add this line:
collectionView.delegate = self
UPDATE
Or if you are using a storyboards, you want to set it using storyboards the same way as you set a dataSource for the dataSource of the tableView:
UPDATE 2
Your tap gesture recognizer cancels event for the collection view, so to deal with this, just uncomment the line tap.cancelsTouchesInView = false, and it will work:
//Looks for single or multiple taps.
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard))
//Uncomment the line below if you want the tap not not interfere and cancel other interactions.
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
I saw your code which you shared in the above answer by #Milan and figured out the reason.
You have added a tap gesture on viewDidLoad of ViewController :
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
This makes the UICollectionView's didSelectItemAt not getting called.
So comment this code and it should work.
For this gesture, you have to find another approach

Resources