Building Peek and Pop with UICollectionView ...and UITableView

Hello, folks! It’s been a long time for me to be away from the thing I Love! Writing.I would never say that if the deal was with a pen and paper 😅.I am back with some proper blogging schedule in my mind, So let’s get started.

Peek and Pop is there in many apps like Instagram and WhatsApp. What’s stopping us from building it in our apps? Having something playful at your fingertips is good, and I would say Peek and Pop is not just an animated feature, but it also gives us an option to quickly add some actions onto our preview without presenting the view controller.

Getting Started

I know setting up the project is quite dull. You can simply skip the Getting Started section by downloading the starter project here and follow along from the next section Peek and Pop.

Let’s set up our project before we can Peek and Pop, Create a new UIKit Project, then in your Main.storyboard file, Embed a Navigation Controller by selecting the ViewController and then in the top bar Click on Editor -> Embed In -> Navigation Controller. Next, Drag and Drop a UICollectionView in your ViewController.

Now, let’s modify the collection view cell by adding a label in it with Horizontally in Container and Vertically in Container constraints. Your final design should look something like the image shown below.

Setting Up Peek and Pop.png

In the Size Inspector, set the Min Spacing to 0 for both cells and lines, as shown in the screenshot below.

Min Spacing Peek and Pop.png

Tip: To add emoji to your Label, press Command + Control + Space

Now, let’s create a UICollectionViewCell subclass, name it PeekAndPopCollectionViewCell. Next, select the cell in your ViewController, and in Attribute Inspector, give it an identifier cell. Next, in your Identity Inspector, add the class you just created in the class field.

Next, select your cell, then open the Assistant Editor and navigate to PeekAndPopCollectionViewCell, create an IBOutlet of the label, and name it emojiLabel.

Next, create an IBOutlet for your collection view in your ViewController.swift file.

Now, Drag and Drop a UIViewController in your Main.storyboard file, and create a UIViewController subclass, name it EmojiViewController. Link your class with the UIViewController you just added, drag and drop a UILabel in it, modify the label to have a font size of 120. Add the constraints for center alignment. Create an IBOutlet for the same and name it emojiLabel.

Your final UI should look like the screenshot shown below.

Peek and Pop Getting Started Final.png

Final Step Add the line given below in your ViewController.swift file.

let emojis = ["😃", "🤣", "😍", "😘", "🥳", "🥺"]

Next, add the line given below in your EmojiViewController.swift file.

var emoji = ""

Note: The code above is to be added as global properties.

Next, in your viewDidLoad() of EmojiViewController.swift file, add the line given below.

self.emojiLabel.text = emoji

We are ready to move to our next section, Peek and Pop.

Peek and Pop

Let’s set up our collection view to Peek and Pop. We will be setting our delegate and data source of the collection view in the viewDidLoad() of the ViewController.swift file.

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.collectionView.delegate = self
    self.collectionView.dataSource = self
    
}

Next, create an extension of ViewController for our delegate and data source functions, as shown below.

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.emojis.count
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = CGSize(width: self.view.frame.width/3, height: self.view.frame.width/3)
        return size
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! PeekAndPopCollectionViewCell
        
        cell.emojiLabel.text = self.emojis[indexPath.item]
        
        return cell
    }
}

We only need two functions more to enable the Peek and Pop feature. Let’s start with the first one.

func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
    
    let config = UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { () -> UIViewController? in
        
        let emojiVC = self.storyboard?.instantiateViewController(withIdentifier: "EmojiViewController") as! EmojiViewController
        emojiVC.emoji = self.emojis[indexPath.item]
        return emojiVC
        
    }, actionProvider: nil)
    return config
}

Here, the function needs a return value of type UIContextMenuConfiguration, and we do that by initializing the configuration with an identifier (takes an object which conforms to NSCopying protocol), previewProvider (here we pass our destination view controller) and actionProvider (this we will keep nil for now, and explore it in the next section).

For identifier, we are passing indexPath and make sure the type is converted to NSIndexPath. As identifier only takes an object which conforms to NSCopying protocol.

In the previewProvider argument, we are executing a block where our destination view controller is instantiated, and the selected index emoji is associated with its emoji property.

Now let’s add our final function, which will do the charm of adding our destination view controller into our navigation stack in case the user wants to open the screen.

func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
    
    guard let vc = animator.previewViewController else { return }
    
    animator.addCompletion {
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

guard here ensures that our previewViewController is not nil before pushing the destination view controller (EmojiViewController).

Let’s run the app and see how it works.

Well! That’s how you Peek and Pop 😁. Now, Let’s look at how do we add actions to them.

Adding Actions

I like how Apple provides us with the actions menu in the preview to get things done quickly, and it doesn’t require much coding. Replace the function below and rerun the app to see the actions live.

func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
    
    let config = UIContextMenuConfiguration(identifier: indexPath as NSIndexPath) { () -> UIViewController? in
        
        let emojiVC = self.storyboard?.instantiateViewController(withIdentifier: "EmojiViewController") as! EmojiViewController
        emojiVC.emoji = self.emojis[indexPath.item]
        return emojiVC
        
    } actionProvider: { (actions) -> UIMenu? in
        
        let okayElement = UIAction(title: "Like", image: nil, identifier: nil) { (action) in
            // Do something
        }
        let menu = UIMenu(title: "", children: [okayElement])
        
        return menu
    }
    
    return config
}

This is all it takes to add an action(s) into our preview. Now, let’s look at the Bonus section to add Peek and Pop functionality into a table view.

Bonus

Adding Peek and Pop in the collection view was easy. With just two functions, we were up and running. Can we achieve the same with UITableView?

Well, Yes, we have similar functions for the table view listed below

func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration?

func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating)

I hope you guys liked the article. Please subscribe to keep me motivated 😉 and to receive weekly updates. I’ll be posting my articles every Tuesday. That will be the day you’ll have to open your Inbox for me. Also, feel free to share your experience with me on Twitter or on Discord.

Download the final project here.

Custom Transition in Swift with present view controller - Non-interactive

Concurrency Programming with Operation Queues - Part 2