HeroTransitions / Hero

Elegant transition library for iOS & tvOS
https://HeroTransitions.github.io/Hero/
MIT License
22.05k stars 1.73k forks source link

Help with re-creating Instagram-like transition? #688

Closed josharnoldjosh closed 3 years ago

josharnoldjosh commented 3 years ago

I am trying to re-create the Instagram transition when viewing a photo.

Transition Description / Goal

When tapping on a photo, the photo expands revealing a new view controller. When dragging/panning the view controller, the view controller shrinks slightly and can be dragged around. When swiped in a certain direction with enough velocity/force, the view controller condenses back into the photo seamlessly.

Link to video of the transition: https://drive.google.com/file/d/16oD3mkzLrE-9aEkS-_AETchvdjzQIA-l/view?usp=sharing

Photos of the transition:

Screen Shot 2020-11-28 at 2 54 38 PM

My progress

When I present the custom UINavigationController, it scales out from the middle of the screen.

When I pan the uinavcontroller, you can see in the image below, it pans well and I can drag it around. However, when I dismiss it, it just scales back into the middle of the screen.

Screen Shot 2020-11-28 at 2 55 43 PM

Link to video of my current progress: https://drive.google.com/file/d/1jOzMUk8n81kx0L0FleugaBMY01LMAEDh/view?usp=sharing

My main problem

The panning / dragging the presented VC around is working, however, presenting and dismissing the UINavigationController simply scales the view controller to zero in the middle of the screen.

I am instead, hoping to "shrink" the view controller, perhaps shrinking the view controller into a square, covering its subviews/content so only the image is visible (just like the Instagram example), and then having this condensed image translate into the original image found on the uitableviewcell? My ultimate question is how can I do this?

How can still keep the dragging/panning of a view controller, with the swipe to dismiss, while also having the view controller shrink its CGRect to only contain the Image, and have this image translate & scale back to the original uitableviewcell image?

Code highlights

My project is linked below so you can see all of the code. However, here is the highlights:

In my first view controller that presents the second view controller:

// First view controller 
class JobsViewController : UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()        
        hero.isEnabled = true
        hero.modalAnimationType = .none
        navigationController?.hero.modalAnimationType = .none
        navigationController?.hero.isEnabled = true        
    }

    // ...

    func myTransition() {
            let vc = ViewJobViewController()
            let nav = PanNavigationController(vc: vc) // my custom nav class
            self.present(nav, animated: true, completion: nil)
    }
}

My second view controller:

class PanNavigationController : UINavigationController {

    init(vc : UIViewController) {
        super.init(rootViewController: vc)
        self.hero.isEnabled = true
        self.modalPresentationStyle = .fullScreen
        self.hero.modalAnimationType = .none
    }

    // ...

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.hero.modifiers = [.scale(0.0)]

        self.view.addPanGesture { (pan) in
            self.handlePan(panGR: pan)
        }
    }

    // ...

    func handlePan(panGR: UIPanGestureRecognizer) {

        let translation = panGR.translation(in: self.view)

        let progress:CGFloat = min(
            (translation.x / 2.0) / view.center.x + abs((translation.y / 2.0) / view.center.y),
            0.5
        )

        let position = CGPoint(x:translation.x/2.0 + view.center.x, y:translation.y/2.0 + view.center.y)

        switch panGR.state {
            case .began:
                hero.dismissViewController()

            case .changed:
                Hero.shared.update(progress)
                Hero.shared.apply(modifiers: [.position(position), .scale(min(1 - (progress / 4.0), 1.02)), .cornerRadius(40), .ignoreSubviewModifiers(recursive: true)], to: view)

                UIView.animate(withDuration: 0.15) {
                    self.presentingViewController?.view.alpha = 0.9
                }

            default:

                UIView.animate(withDuration: 0.15) {
                    self.presentingViewController?.view.alpha = 1.0
                }

                if (progress + panGR.velocity(in: self.view).x + panGR.velocity(in: self.view).y) > 0 {
                    Hero.shared.finish()
                } else {
                    Hero.shared.cancel()
                }
            }
      }

}

Any help would be greatly appreciated! Thanks!

General Information

Demo Project

https://github.com/josharnoldjosh/HeroDemo