UIHostingController auto layout during UIViewControllerTransitioningDelegate?

627 Views Asked by At

I have the following code:

import UIKit
import SwiftUI



class ViewController: UIViewController {

    @IBOutlet weak var testButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    
    @IBOutlet weak var swiftuiButton: UIButton!
    
    @IBAction func didTap(_ sender: Any) {
        
        let storyboard = UIStoryboard(name: "DetailsVC", bundle: .main)
        let vc = storyboard.instantiateInitialViewController() as! DetailsVC
        vc.modalPresentationStyle = .custom
        vc.transitioningDelegate = self
        present(vc, animated: true)
        
    }
    
    @IBAction func swiftUITest(_ sender: Any) {
        let swiftUIView = SwiftUIView() {
            self.dismiss(animated: true)
        }
        let hostingController = UIHostingController.init(rootView: swiftUIView)
        hostingController.modalPresentationStyle = .custom
        hostingController.transitioningDelegate = self
        present(hostingController, animated: true)
    }
    
}

struct SwiftUIView: View {

    var didTapClose:()->()
    
    var body: some View {
        GeometryReader { geo in
            ZStack {
                
                Color.yellow.ignoresSafeArea()
                
                Text("SWIFTUI VIEW")
                    .foregroundColor(.black)
                
            }
            .transaction { transaction in
                transaction.animation = nil
            }
            .frame(width: geo.size.width, height: geo.size.height)
            .border(.red)
            .onTapGesture {
                didTapClose()
            }
        }
    }

}


extension ViewController:UIViewControllerTransitioningDelegate {
    
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return ToTileDetailsAnimator(fromFrame: testButton.frame, duration: 5)
    }
    
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return nil
    }
    
    func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return nil
    }
    
}

final class ToTileDetailsAnimator:NSObject, UIViewControllerAnimatedTransitioning {
    
    fileprivate(set) var fromFrame:CGRect
    fileprivate(set) var duration:Double
    
    init(fromFrame:CGRect, duration:Double) {
        self.fromFrame = fromFrame
        self.duration = duration
    }
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
        guard let fromVC = transitionContext.viewController(forKey: .from) else { return }
        let finalFrame = fromVC.view.frame.insetBy(dx: 20, dy: 20)
        
        guard let toVC = transitionContext.viewController(forKey: .to)  else { return }
        
        let containerView = transitionContext.containerView
        
        containerView.addSubview(toVC.view)
        
        toVC.view.frame = fromFrame
        toVC.view.clipsToBounds = true
        toVC.view.layer.cornerRadius = 6
        toVC.view.layoutIfNeeded()
        
        fromVC.beginAppearanceTransition(false, animated: true)
        toVC.beginAppearanceTransition(true, animated: true)
        toVC.view.alpha = 0
        
        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseInOut, animations: {
            
            toVC.view.frame = finalFrame
            toVC.view.alpha = 1
            toVC.view.layoutIfNeeded()

        }) { (completed) in
            toVC.view.frame = finalFrame
            toVC.view.layoutIfNeeded()
            toVC.view.alpha = 1
            fromVC.endAppearanceTransition()
            toVC.endAppearanceTransition()
            let transitionSuccess = !transitionContext.transitionWasCancelled
            transitionContext.completeTransition(transitionSuccess)
        }
    }
    
}

Issue:

When presenting a UIViewController all works great. When presenting a UIHostingController the transition animation shows the SwiftUIView NOT growing and like the UIKit case correctly does.

Why is the SwiftUIView not scaling up during the animation? Why is it at full size immediately?

This is what the transition animations look like:

enter image description here

0

There are 0 best solutions below