Implementing Shared Element Transitions with Hero Library in iOS Swift

Shared Element Transition with Hero Library in iOS Swift

In this article, we will explore how to implement a shared element transition between two view controllers in an iOS app using the Hero Library. We’ll dive into the basics of shared element transitions, the Hero Library, and provide step-by-step instructions on how to achieve this effect.

What is a Shared Element Transition?

A shared element transition is a type of animation where a view controller presents a new view controller while maintaining the visual state of the previous view controller. This can include elements such as text, images, or even entire sections of the screen. The goal of shared element transitions is to create an intuitive and immersive user experience.

What is Hero Library?

Hero Library is an open-source library for managing hero animations in iOS apps. It provides a simple and elegant way to add animations to your app’s navigation between view controllers. With Hero Library, you can customize the animation type, duration, and even add custom transitions.

Setting Up Hero Library

Before we dive into implementing shared element transitions, let’s set up Hero Library in our project. To do this, we’ll need to install the library using CocoaPods or by adding it directly to our project.

# Install Hero Library using CocoaPods

pod 'Hero'

After installing Hero Library, we need to import it into our ViewController and enable hero animations for our UINavigationController.

Enabling Hero Animations

To enable hero animations, we need to add the following line of code:

navigationController?.hero.isEnabled = true

This will enable hero animations for all view controllers that are part of the UINavigationController. We can also customize the animation type and duration by using different classes provided by the Hero Library.

Implementing Shared Element Transition

Now that we have Hero Library set up, let’s implement a shared element transition between our table view controller and detail view controller.

Table View Controller

First, let’s modify our table view controller to present the detail view controller with a shared element transition.

import UIKit

class TableViewController: UIViewController {

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // Get the image URL from the selected row
        guard let imageUrl = images[indexPath.row] else { return }

        // Create a hero configuration for the detail view controller
        let heroConfiguration = HeroConfig()
            .size(800)
            .position(.centerX)
            .duration(1.0)
            .springiness(1.0)

        // Present the detail view controller with a shared element transition
        present(UINavigationController(rootViewController: DetailViewController(imageUrl)), animated: true, completion: nil)
    }
}

Detail View Controller

Next, let’s modify our detail view controller to receive the shared element and display it.

import UIKit

class DetailViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Get the image URL from the navigation controller
        guard let imageUrl = navigationController?.hero?.config else { return }

        // Create a hero view for the detail view controller
        let heroView = HeroView(frame: CGRect(x: 0, y: 0, width: imageUrl.size.width, height: imageUrl.size.height))
            .image(imageUrl)
            .position(x: imageUrl.size.width / 2, y: imageUrl.size.height / 2)

        // Add the hero view to the detail view controller's view
        view.addSubview(heroView)

        // Configure the hero view for animation
        heroConfiguration = HeroConfig()
            .size(800)
            .duration(1.0)
            .springiness(1.0)
    }

    var heroConfiguration: HeroConfig?

    func updateHero() {
        // Update the hero view's position and size
        guard let configuration = heroConfiguration else { return }
        heroView?.frame = CGRect(x: configuration.size.width / 2, y: configuration.position.y, width: configuration.size.width, height: configuration.size.height)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // Update the hero view's position and size
        updateHero()
    }
}

Conclusion

In this article, we explored how to implement a shared element transition between two view controllers in an iOS app using the Hero Library. We covered the basics of shared element transitions, set up Hero Library, enabled hero animations for our UINavigationController, and implemented a shared element transition between our table view controller and detail view controller.

By following these steps, you can add animations to your app’s navigation between view controllers and create a more immersive user experience.

Additional Tips and Variations

Using Custom Transitions

One of the most powerful features of Hero Library is its ability to customize transitions. You can use different classes provided by the library to create custom transitions or even override the default transition behavior.

import UIKit

class TableViewController: UIViewController {

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // Get the image URL from the selected row
        guard let imageUrl = images[indexPath.row] else { return }

        // Create a custom hero configuration for the detail view controller
        let heroConfiguration = CustomHeroConfig()
            .size(800)
            .duration(1.0)
            .springiness(1.0)

        // Present the detail view controller with a shared element transition
        present(UINavigationController(rootViewController: DetailViewController(imageUrl)), animated: true, completion: nil)
    }
}

Using Storyboard Segues

Another option for implementing shared element transitions is to use storyboard segues. While this approach is less flexible than using Hero Library, it can be a good choice when you need to implement complex animations or don’t want to add additional dependencies to your project.

import UIKit

class TableViewController: UIViewController {

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // Get the image URL from the selected row
        guard let imageUrl = images[indexPath.row] else { return }

        // Present the detail view controller with a storyboard segue
        performSegue(withIdentifier: "detailSegue", sender: self)
    }
}
import UIKit

class DetailViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Get the image URL from the navigation controller's segue data
        guard let imageUrl = navigationController?.segueData()?.object as? ImageURL else { return }

        // Create a hero view for the detail view controller
        let heroView = HeroView(frame: CGRect(x: 0, y: 0, width: imageUrl.size.width, height: imageUrl.size.height))
            .image(imageUrl)
            .position(x: imageUrl.size.width / 2, y: imageUrl.size.height / 2)

        // Add the hero view to the detail view controller's view
        view.addSubview(heroView)

        // Configure the hero view for animation
        let heroConfiguration = HeroConfig()
            .size(800)
            .duration(1.0)
            .springiness(1.0)
    }

    var heroConfiguration: HeroConfig?

    func updateHero() {
        // Update the hero view's position and size
        guard let configuration = heroConfiguration else { return }
        heroView?.frame = CGRect(x: configuration.size.width / 2, y: configuration.position.y, width: configuration.size.width, height: configuration.size.height)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // Update the hero view's position and size
        updateHero()
    }
}

By following these tips and variations, you can customize your shared element transitions to fit your app’s unique needs.


Last modified on 2024-05-03