Unbalanced calls to begin/end appearance transitions for <****.ViewController: 0x7fa425f07120>
Asked Answered
E

2

0

I have seen a lot of other stackoverflow posts related to this error, but I'm not able to understand it (and thus, don't know how to solve) in my case.

Most of the responses in stackoverflow posts (like this one, for example) involve multiple view controllers and how you push one view controller into the stack before the previous one is finished.

But in my case, I only have a single view controller with very very very minimal UI - I got this error in a sample test code.

There's only one view controller in the project,

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        NSLog(TAG + "ViewController.viewDidLoad")
        
        super.viewDidLoad()
        view.backgroundColor = .systemBlue
    }
}

The view controller is initialized in scene(willConnectTo:options) and the UI is displayed as shown below. But there is a custom class (Presentation) in between.

This is my scene(willConnectTo:options)

// uses Presentation class to display UI
func scene(_ pUIScene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        NSLog(TAG + "SceneDelegate.scene(willConnectTo:options)")
        
        AppDelegate.sForegroundScene = pUIScene
        
        var user_interface = Presentation()
        
        user_interface.CreateWindow()
    }

and this is my Presentation class,

class Presentation {
    
    var window: UIWindow?
    
    init() {
        NSLog(TAG + "Presentation.init")
    }
    
    func CreateWindow() {
        NSLog(TAG + "Presentation.CreateWindow")
        
        guard let winScene = (AppDelegate.sForegroundScene as? UIWindowScene) else {
            NSLog(TAG + "Error in obtaining UIWindowScene!")
            return
        }
       
        window = UIWindow(windowScene: winScene)

        window?.rootViewController = ViewController()

        window?.makeKeyAndVisible()
    }
}

Now, if I remove the Presentation class and directly initialise the ViewController and set UI in scene(willConnectTo:option) - as shown below, it works as expected - I get a blue screen.

// Presentation class is not used
func scene(_ pUIScene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        NSLog(TAG + "SceneDelegate.scene(willConnectTo:options)")
        
        AppDelegate.sForegroundScene = pUIScene

        guard let winScene = (AppDelegate.sForegroundScene as? UIWindowScene) else {
            NSLog(TAG + "Error in obtaining UIWindowScene!")
            return
        }

        window = UIWindow(windowScene: winScene)

        window?.rootViewController = ViewController()

        window?.makeKeyAndVisible()
        
    }

Why does moving the UI code to a different class cause this 'Unbalanced Calls to begin/end appearance transitions' issue??

Estriol answered 12/10, 2022 at 7:29 Comment(0)
E
0

Its because I was trying to set my view controller to a random window variable (in Presentation) and not the one associated with the SceneDelegate (and thus, the scene).

After the following changes, it worked.

In SceneDelegate, associate the window to the scene object, which can later be retrieved by Presentation.

func scene(_ pUIScene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        NSLog(TAG + "SceneDelegate.scene(willConnectTo:options)")
        
        guard let winScene:UIWindowScene = (pUIScene as? UIWindowScene) else {
            NSLog((TAG + "Error in obtaining UIWindowScene"))
            return
        }

        window = UIWindow(windowScene:winScene)        
        AppDelegate.sForegroundScene = winScene

        
        let user_interface = Presentation()

        user_interface.CreateWindow()
    }

In Presentation,

func CreateWindow() {
        NSLog(TAG + "Presentation.CreateWindow")
        
        guard var winScene:UIWindowScene = AppDelegate.sForegroundScene else {
            NSLog(TAG + "Unable to obtain UIWindowScene")
            return
        }

        NSLog(TAG + "Number of windows = %d", winScene.windows.count) // Logs 1 because the window is associated with the scene in SceneDelegate
        
        window = winScene.windows.first

        window?.rootViewController = ViewController()

        window?.makeKeyAndVisible()
    }
Estriol answered 20/10, 2022 at 6:37 Comment(0)
I
0

This was a real puzzle, as I was getting this error in even the most base case

window = UIWindow(windowScene: windowScene)

let dummy = UIViewController()
window?.rootViewController = dummy

It turns out the SceneDelegate needs a class reference to hang on to the window, and without it, the issue will plague even the most basic case.

final class SceneDelegate: NSObject, UIWindowSceneDelegate {
        
    var window: UIWindow?

Keeping this reference to the window at the self level is what ultimately made my app load correctly.

Inspan answered 30/6, 2023 at 17:59 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.