Xcode 13 Beta 5 Error: UIviewcontroller Is Missing Its Initial Trait Collection Populated During Initialization

Xcode 13 beta 5 error: UIViewController is missing its initial trait collection populated during initialization

After commenting out a lot of code, I started getting a new error:

fatal error: Use of unimplemented initializer ‘init(style:)’ for class ‘PaymentPickerViewController’

Looking into the behaviour some more, it seems we have an objc initialized extension. I believe in this Xcode 13, the objc code gets mangled and doesn't "see" the root call to the initializer.

Our original init code path was this:

@objc extension PaymentPickerViewController {
convenience init() {
dataProvider = DataProvider()
self.init(dataProvider: dataProvider)
}
}

init(dataProvider: DataProvider) {
self.dataProvider = dataProvider
super.init(style: .plain)
}

The fix was explicitly overriding the swift init we wanted to use from the super:

override init(style: UITableView.Style) {
super.init(style: .plain)
self.dataProvider = DataProvider()
}

This forces the objc to see the correct init and then it works.

I fear the original error that I posted is very misleading so hopefully this helps anyone who runs into this issue.

UITraitCollection getter, which is not supported on iOS13

I found the solution. When I slidingPage initiate then I show a preloader. My crash has occurred when I show the preloader. When I put my preloader in DispatchAsyncAfter like below I could fix my error:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self addPreloder];

__weak typeof(self) weakSelf = self;
[self sd_setImageWithURL:url
placeholderImage:placeholder
options:options
progress:progressBlock
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageUrl) {
if (completedBlock) {
completedBlock(image, error, cacheType, imageUrl);
}

dispatch_async(dispatch_get_main_queue(), ^(void) {

if (weakSelf.preloding) {

dispatch_async(dispatch_get_main_queue(), ^(void) {
[weakSelf.preloding stopAnimating];
[weakSelf.preloding removeFromSuperview];
[weakSelf removeActivityIndicator];
});

}

});
}
];
});

How to override trait collection for initial UIViewController? (with Storyboard)

Ok, I wish there was another way around this, but for now I just converted code from the Apple example to Swift and adjusted it to use with Storyboards.

It works, but I still believe it is an awful way to archive this goal.

My TraitOverride.swift:

import UIKit

class TraitOverride: UIViewController {

required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

var forcedTraitCollection: UITraitCollection? {
didSet {
updateForcedTraitCollection()
}
}

override func viewDidLoad() {
setForcedTraitForSize(view.bounds.size)
}

var viewController: UIViewController? {
willSet {
if let previousVC = viewController {
if newValue !== previousVC {
previousVC.willMoveToParentViewController(nil)
setOverrideTraitCollection(nil, forChildViewController: previousVC)
previousVC.view.removeFromSuperview()
previousVC.removeFromParentViewController()
}
}
}

didSet {
if let vc = viewController {
addChildViewController(vc)
view.addSubview(vc.view)
vc.didMoveToParentViewController(self)
updateForcedTraitCollection()
}
}
}

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator!) {
setForcedTraitForSize(size)
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
}

func setForcedTraitForSize (size: CGSize) {

let device = traitCollection.userInterfaceIdiom
var portrait: Bool {
if device == .Phone {
return size.width > 320
} else {
return size.width > 768
}
}

switch (device, portrait) {
case (.Phone, true):
forcedTraitCollection = UITraitCollection(horizontalSizeClass: .Regular)
case (.Pad, false):
forcedTraitCollection = UITraitCollection(horizontalSizeClass: .Compact)
default:
forcedTraitCollection = nil
}
}

func updateForcedTraitCollection() {
if let vc = viewController {
setOverrideTraitCollection(self.forcedTraitCollection, forChildViewController: vc)
}
}

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
performSegueWithIdentifier("toSplitVC", sender: self)
}

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "toSplitVC" {
let destinationVC = segue.destinationViewController as UIViewController
viewController = destinationVC
}
}

override func shouldAutomaticallyForwardAppearanceMethods() -> Bool {
return true
}

override func shouldAutomaticallyForwardRotationMethods() -> Bool {
return true
}
}

To make it work you need to add a new UIViewController on the storyboard and made it the initial. Add show segue from it to your real controller like this:
storyboard

You need to name the segue "toSplitVC":
segue name

and set initial controller to be TraitOverride:
assign controller

Now it should work for you too. Let me know if you find a better way or any flaws in this one.

ios Swift fatal error: use of unimplemented initializer 'init()'

“Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.”

Automatic Initializer Inheritance

  • Rule 1:
    If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
  • Rule 2:
    If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/tw/jEUH0.l

Since you have override the init(coder aDecoder: NSCoder), TimeLineTableViewController won't have the init() initiailzer.

You can provide an implementation of all of its superclass designated initialisers like this

override init() {
super.init()
}

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}

required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

, or just delete the implementation of init(coder aDecoder: NSCoder).

Attempt to present UIViewController on UIViewController whose view is not in the window hierarchy

Where are you calling this method from? I had an issue where I was attempting to present a modal view controller within the viewDidLoad method. The solution for me was to move this call to the viewDidAppear: method.

My presumption is that the view controller's view is not in the window's view hierarchy at the point that it has been loaded (when the viewDidLoad message is sent), but it is in the window hierarchy after it has been presented (when the viewDidAppear: message is sent).


Caution

If you do make a call to presentViewController:animated:completion: in the viewDidAppear: you may run into an issue whereby the modal view controller is always being presented whenever the view controller's view appears (which makes sense!) and so the modal view controller being presented will never go away...

Maybe this isn't the best place to present the modal view controller, or perhaps some additional state needs to be kept which allows the presenting view controller to decide whether or not it should present the modal view controller immediately.



Related Topics



Leave a reply



Submit