SwiftUI View in UIHostingController renders incorrectly during frame animation if there's an internal animation

Viewed 531

When animating a frame change for a UIHostingController, the rendered View within will not animate a size change if it currently has an internal animation in progress.

Is this a known issue or should I be doing something different? I have to imagine this is a pretty common use case. I've tested this on iOS 13.3 and 14.2.

Here's a screen capture of the issue: https://www.loom.com/share/91eb220dad244c848161330b574e22df

Notice that you see the yellow UIView background during the frame animation, although you should never see it. The rendered SwiftUI View immediately changes to the final frame size and only animates the origin.

import UIKit
import SwiftUI


// When `true`, issue presents itself.
let animate = true


struct SwiftUIView: View {

    @State var appeared = false

    let animation = Animation
        .default
        .repeatForever(autoreverses: false)

    var body: some View {
        ZStack {
            Color.pink
            Image(systemName: "rays")
                .resizable()
                .frame(width: 50, height: 50)
                .rotationEffect(appeared ? .degrees(360) : .zero)
                .onAppear {
                    withAnimation(animation) {
                        if animate {
                            appeared = true
                        }
                    }
                }
        }
    }
}


class ViewController: UIViewController {

    var vc: UIViewController!

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .blue
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTap)))

        vc = UIHostingController(rootView: SwiftUIView())
        vc.view.backgroundColor = .yellow

        self.addChild(vc)
        view.addSubview(vc.view)
        vc.view.frame = CGRect(x: 50, y: 50, width: 400, height: 100)
        vc.didMove(toParent: self)
    }

    @objc private func didTap() {
        UIView.animate(withDuration: 4) {
            self.vc.view.frame = CGRect(x: 300, y: 300, width: 100, height: 400)
            self.view.layoutIfNeeded()
        }
    }
}

0 Answers
Related