You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现自定义转场效果?请提供VC1到VC2的Logo转场简易示例

Hey there! I’ve done this exact kind of transition a bunch of times—let’s walk through a simple, native implementation to get your logo smoothly moving from VC1 to VC2, no third-party libraries required.

Core Idea

The trick is to create a snapshot of the logo from VC1, animate that snapshot to the position of the logo in VC2, then swap it out for VC2’s actual logo once the animation finishes. This keeps both VCs’ original logos intact while giving the illusion of a single moving element.

Step-by-Step Implementation

Let’s break this down into code (using Swift for iOS, but the logic translates to Objective-C too).

1. Create the Custom Transition Animator

This class handles all the animation logic. It conforms to UIViewControllerAnimatedTransitioning to define the animation duration and the actual animation steps.

import UIKit

class LogoTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    // Define how long the animation lasts
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.6 // Adjust this to make the animation faster/slower
    }

    // The main animation logic
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        // Safely get references to both view controllers and the container view
        guard let fromVC = transitionContext.viewController(forKey: .from) as? ViewController1,
              let toVC = transitionContext.viewController(forKey: .to) as? ViewController2,
              let containerView = transitionContext.containerView else {
            transitionContext.completeTransition(false)
            return
        }

        // 1. Take a snapshot of VC1's logo (this is what we'll animate)
        let logoSnapshot = fromVC.logoImageView.snapshotView(afterScreenUpdates: true)!
        let startFrame = containerView.convert(fromVC.logoImageView.frame, from: fromVC.view)
        logoSnapshot.frame = startFrame
        containerView.addSubview(logoSnapshot)

        // 2. Prepare VC2's view: add it to the container, but hide its logo temporarily
        toVC.view.frame = transitionContext.finalFrame(for: toVC)
        toVC.logoImageView.isHidden = true
        containerView.addSubview(toVC.view)

        // 3. Calculate where VC2's logo lives in the container view's coordinate system
        let targetFrame = containerView.convert(toVC.logoImageView.frame, from: toVC.view)

        // 4. Run the animation!
        UIView.animate(withDuration: transitionDuration(using: transitionContext),
                       delay: 0,
                       usingSpringWithDamping: 0.8, // Optional spring effect for a "bouncy" feel
                       initialSpringVelocity: 0.3,
                       options: .curveEaseInOut) {
            // Move the snapshot to VC2's logo position, add a slight scale for flair
            logoSnapshot.frame = targetFrame
            logoSnapshot.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
        } completion: { _ in
            // Clean up: show VC2's real logo, remove the snapshot, mark transition as done
            toVC.logoImageView.isHidden = false
            logoSnapshot.removeFromSuperview()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}

2. Set Up ViewController1 (Source VC)

VC1 will have your initial logo, and trigger the transition to VC2. We’ll also make it the transition delegate to use our custom animator.

import UIKit

class ViewController1: UIViewController {
    // Your logo image view
    let logoImageView: UIImageView = {
        let iv = UIImageView(image: UIImage(named: "your-logo")) // Replace with your logo asset name
        iv.contentMode = .scaleAspectFit
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupLogoLayout()
    }

    // Position the logo in VC1 (adjust constraints to match your layout)
    private func setupLogoLayout() {
        view.addSubview(logoImageView)
        NSLayoutConstraint.activate([
            logoImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
            logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            logoImageView.widthAnchor.constraint(equalToConstant: 120),
            logoImageView.heightAnchor.constraint(equalToConstant: 120)
        ])
    }

    // Trigger transition (connect this to a button tap, for example)
    @IBAction func navigateToVC2(_ sender: UIButton) {
        let vc2 = ViewController2()
        vc2.transitioningDelegate = self
        vc2.modalPresentationStyle = .fullScreen // Required for custom transitions
        present(vc2, animated: true)
    }
}

// MARK: - Transition Delegate
extension ViewController1: UIViewControllerTransitioningDelegate {
    // Return our custom animator when presenting VC2
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return LogoTransitionAnimator()
    }

    // Optional: Add a reverse animation for dismissing VC2 (mirror the logic above)
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return nil // Remove this and add reverse logic if you want a return animation
    }
}

3. Set Up ViewController2 (Destination VC)

VC2 will have the logo in its final position—we just need to make sure it’s hidden until the animation finishes.

import UIKit

class ViewController2: UIViewController {
    // Your logo image view (same asset as VC1, different position)
    let logoImageView: UIImageView = {
        let iv = UIImageView(image: UIImage(named: "your-logo"))
        iv.contentMode = .scaleAspectFit
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .lightGray
        setupLogoLayout()
    }

    // Position the logo in VC2 (adjust constraints to match your target layout)
    private func setupLogoLayout() {
        view.addSubview(logoImageView)
        NSLayoutConstraint.activate([
            logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            logoImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            logoImageView.widthAnchor.constraint(equalToConstant: 200),
            logoImageView.heightAnchor.constraint(equalToConstant: 200)
        ])
    }

    // Return to VC1 (connect to a button tap)
    @IBAction func goBackToVC1(_ sender: UIButton) {
        dismiss(animated: true)
    }
}
Key Notes
  • Snapshot vs. Moving the Original View: Using a snapshot ensures VC1’s logo stays in place—if you moved the original UIImageView, VC1 would lose its logo after the transition.
  • Coordinate System Conversion: Always use containerView.convert(_:from:) to get the correct frame positions, since each view controller’s view has its own coordinate system.
  • Customization: Tweak the animation duration, spring parameters, or add opacity changes to match your app’s style.
  • Push/Pop Instead of Present/Dismiss: If you’re using a UINavigationController, set the nav controller’s delegate and implement navigationController(_:animationControllerFor:from:to:) to return your animator.

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:19:42