We’ve seen a feature in our iOS devices home screen on long press to remove an app and drag and drop any of the app. So, I have written this blog to show how we can create this feature in our app while development.
I have used UICollectionView DataSources and Delegates to implement this functionality with a shaking animation.
Here is my view that looks like with few sets of images in a UICollectionView:
1. UILongPressGestureRecognizer
Add UILongPressGestureRecognizer on UICollectionView in viewDidLoad:
1 2 3 |
//adding longpress gesture over UICollectionView longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(self.longTap(_:))) imgcollection.addGestureRecognizer(longPressGesture) |
UILongPressGestureRecognizer action func definition as given below:
Different states of Long press is given below:
- beginInteractiveMovementForItem(at indexPath: IndexPath) -> It helps to know the interactive movement of the cell at particular index path.
- updateInteractiveMovementTargetPosition(_:) -> It helps to update the item position in the collection view with the help of gesture position.
- endInteractiveMovement()-> It helps to know that the interactive movement is ended and long press gesture has been released.
- cancelInteractiveMovement()-> It helps to know the interactive movement has ended.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@objc func longTap(_ gesture: UIGestureRecognizer){ switch(gesture.state) { case .began: guard let selectedIndexPath = imgcollection.indexPathForItem(at: gesture.location(in: imgcollection)) else { return } imgcollection.beginInteractiveMovementForItem(at: selectedIndexPath) case .changed: imgcollection.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view!)) case .ended: imgcollection.endInteractiveMovement() doneBtn.isHidden = false longPressedEnabled = true self.imgcollection.reloadData() default: imgcollection.cancelInteractiveMovement() } } |
2. UICollectionView Delegates and DataSources :
imgArr is an array of [String] type. The array consists of a set of sample images.
1 2 3 |
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return imgArr.count } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SmallImgCell.identifier, for: indexPath) as! SmallImgCell cell.backgroundColor = UIColor.clear cell.imgView.image = UIImage(named: "\(imgArr[indexPath.row])") cell.removeBtn.addTarget(self, action: #selector(removeBtnClick(_:)), for: .touchUpInside) if longPressedEnabled { cell.startAnimate() }else{ cell.stopAnimate() } return cell } |
1 2 3 |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: UIScreen.main.bounds.size.width/4 - 20, height: UIScreen.main.bounds.size.width/4 - 20) } |
3. SmallImgCell : UICollectionViewCell :
UICollectionViewCell consisting of an image view and a remove button.
The UIImageView is for displaying an image over the UICollectionView and UIButton for showing the cross button over the cell for deleting an item from collection view.
1 2 3 4 |
@IBOutlet weak var imgView: UIImageView! @IBOutlet weak var removeBtn: UIButton! var isAnimate: Bool! = true |
startAnimate snippet for animation over image in collection view:
The func will start toggling of the images.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//Animation of image func startAnimate() { let shakeAnimation = CABasicAnimation(keyPath: "transform.rotation") shakeAnimation.duration = 0.05 shakeAnimation.repeatCount = 4 shakeAnimation.autoreverses = true shakeAnimation.duration = 0.2 shakeAnimation.repeatCount = 99999 let startAngle: Float = (-2) * 3.14159/180 let stopAngle = -startAngle shakeAnimation.fromValue = NSNumber(value: startAngle as Float) shakeAnimation.toValue = NSNumber(value: 3 * stopAngle as Float) shakeAnimation.autoreverses = true shakeAnimation.timeOffset = 290 * drand48() let layer: CALayer = self.layer layer.add(shakeAnimation, forKey:"animate") removeBtn.isHidden = false isAnimate = true } |
stopAnimate snippet for animation over image in collection view:
The func will stop toggling of images.
1 2 3 4 5 6 |
func stopAnimate() { let layer: CALayer = self.layer layer.removeAnimation(forKey: "animate") self.removeBtn.isHidden = true isAnimate = false } |
Remove Button and done button is shown like in iPhone X :
On clicking Remove button action remove the item at that index from UICollectionView and reload it.
On Done button action hide the done button, stop the animation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@IBAction func removeBtnClick(_ sender: UIButton) { let hitPoint = sender.convert(CGPoint.zero, to: self.imgcollection) let hitIndex = self.imgcollection.indexPathForItem(at: hitPoint) //remove the image and refresh the collection view self.imgArr.remove(at: (hitIndex?.row)!) self.imgcollection.reloadData() } @IBAction func doneBtnClick(_ sender: UIButton) { //disable the shake and hide done button doneBtn.isHidden = true longPressedEnabled = false self.imgcollection.reloadData() } |
For changing the cell locations from one place to another or dragging the cells write following snippets:
4. UICollectionView Delegates use to drag and drop from target to destination :
func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool
–> Return true for enabling the drag feature.
–>
func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
–> Swap item from source to destination
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool { return true } func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { print("Start index :- \(sourceIndexPath.item)") print("End index :- \(destinationIndexPath.item)") let tmp = imgArr[sourceIndexPath.item] imgArr[sourceIndexPath.item] = imgArr[destinationIndexPath.item] imgArr[destinationIndexPath.item] = tmp imgcollection.reloadData() } |
I hope from this, it will make you more comfortable using a LongPress and drag and drop feature of UICollectionView. Thanks for tuning in once again!