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:
You need to name the segue "toSplitVC":
and set initial controller to be TraitOverride:
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
Reachability Change Notification Should Be Called Only Once
Ambiguous Reference to Member 'Buildblock()'
Module Compiled with Swift X.1 Cannot Be Imported in Swift X.0.2
Ckasset in Server Record Contains No Fileurl, Cannot Even Check for Nil
How to Use Crc32 from Zlib in Swift (Xcode 9)
Strange String.Unicodescalars and Characterset Behaviour
Swift Navigation Bar Item Not Calling Action
Implement Protocol with Different Associated Type
Pfobject Unable to Be Cast to Custom Subclass
"The Requested Snapshot Version Is Too Old." Error in Firestore
Swift Increment Int! Not Working
Difference Between Object(Forkey:) and Value(Forkey:) in Userdefaults
Swiftui CPU High Usage on Real-Time Foreach View Updating (Macos)
Set an Horizontal Scroll to My Barchart in Swift
Pass Type to Generic Function and Compare