viewWillTransition is giving the wrong size

2.1k views Asked by At

I am making a custom keyboard. I want to generate the keys'(UIButtons) width and height based on the view's width and height.

When the keyboard is loaded initially, viewDidAppear correctly determines the height of the view. (375 x 216)

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.renderKeys()
    }

enter image description here

When rotating to Landscape, it invokes viewWillTransition

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    NSLog("toSize \(size.width) x \(size.height)")

    coordinator.animate(alongsideTransition: {(_ context: UIViewControllerTransitionCoordinatorContext) -> Void in
        NSLog("animating \(self.view.frame.width) x  \(self.view.frame.height)")

    }, completion: {(_ context: UIViewControllerTransitionCoordinatorContext) -> Void in

        NSLog("animationCompleted \(self.view.frame.width) x  \(self.view.frame.height)")
        self.renderKeys()
    })

}

enter image description here

Rotating back to Portrait results this.

enter image description here

What I found is that viewWillTransition's to size isn't correctly determining the size that the view will be or I am just misunderstanding the usage of the function. Even after the animation, I am not able to get right width and height of the view.

Here is the debug log in order.

[28651:1348399] Calling viewDidAppear

[28651:1348399] Cleaning keys
[28651:1348399] toSize 667.0 x 216.0
[28651:1348399] animating 667.0 x  216.0
[28651:1348399] animationCompleted 667.0 x  216.0

[28651:1348399] Cleaning keys
[28651:1348399] toSize 375.0 x 162.0
[28651:1348399] animating 375.0 x  162.0
[28651:1348399] animationCompleted 375.0 x  162.0
1

There are 1 answers

0
eharo2 On

I am working in a custom keyboard as well and found a similar problem.

After several hours experimenting, I have learned the following:

  • When you run the keyboard as an app, the views are loaded only once, and the app life cycle is completed as expected (application(didFinishLaunchingWithOptions:), applicationDidBecomeActive), etc are executed)
  • All orientation transitions can be captured in viewWillTransition(to size:). e.g. in an iPhone 8+ this will report size of (414.0, 736.0) or (736.0, 414.0)

Now, running the project as a Keyboard Target: -

  • The system does not follow the regular app life cycle: application(didFinishLaunchingWithOptions:), applicationDidBecomeActive, etc. are not reported
    • Every time the device changes from vertical to horizontal or viceversa, the main ViewController is loaded.
    • Trying to capture the viewSize in viewDidLoad can be tricky, given that you may need a delay (using Timer.scheduledTimer(withTimeInterval:), at least in the first iteration
    • The viewSize is reported in updateViewConstraints(), but the size seems to be not completely reliable. After each transition to Portrait, it can report the size to be 414 x 736 (ScreenSize) or 414 x 226 (correct keyboard size). In the case of an iPhone 8+
    • Given this particular management of the app life cycle, the viewWillTransition method is not executed when going from Portrait to Landscape or from Landscape to Portrait (I guess this is related to the view life cycle that is interrupted in every rotation).
    • It is executed when going from LandscapeLeft to LandscapeRight (through UpsideDown, that is not enabled), and it reports the correct size (736 x 162 in an iPhone 8+)

So, What I am doing is to capture the screen size in viewDidLoad after a delay and forget about updateViewConstraints or viewWillTransition(to size:)

Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { (nil) in
    print("Load Size: \(self.view.frame.size)")
}

NOTE* This delay seems to be needed only in the first load (in this case, even updateViewConstraints is executed before viewDidLoad. In following iterations of viewDidLoad, it is not required. Also. You may want to experiment the delay, and test in the actual devices vs. the simulator

Reference table:

Device - (Screen) - (KeyboardPortrait) - (KeyboardLandscape)

iPhone X - (375 x 812) - (375 x 141) - (662 x 131)

iPhone 8+ - (414 x 736) - (414 x 226) - (736 x 162)

iPhone 7+/8+ - (414 x 736) - (414 x 226) - (736 x 162)

iPhone 7/8 - (375 x 667) - (375 x 216) - (667 x 162)

6/6s/6+/6s+ - Same as iPhone 7/8

iPhone SE - (320 x 568) - (320 x 216) - (568 x 162)

I hope it helps. Regards... e