viewDidLoad() not called - swift

Either there is something weird in my project (Mojave, XCode10) or I am missing something very basic.
This is my whole code:
import Foundation
import Cocoa
class ViewController: NSViewController {
public init () {
super.init(nibName: nil, bundle: nil)
self.view = ConfigView(rect: NSRect(x: 0, y: 0, width: 400, height: 300))
print("2")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
print("3")
}
override func viewDidLoad() {
super.viewDidLoad()
print("4")
}
override func viewWillAppear() {
super.viewWillAppear()
print("5")
}
override func viewDidAppear() {
super.viewDidAppear()
print("6")
print("Is loaded: \(isViewLoaded)")
}
}
class ConfigView: NSView {
public init(rect: NSRect) {
super.init(frame: rect)
print("1")
}
required public init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I want to setup stuff in the viewDidLoad() method, but this method is never called. The console shows:
1
2
5
6
Is loaded: true
So, obviously loadView() and viewDidLoad() are not called, but afterwards when the view appeared isViewLoaded() shows true.
Why is that lifecycle method being ignored?

I suspect what is happening here is that since you are initializing the view property in the init function, when loadView() is called, it detects that the view has already been loaded and therefore does not call the viewDidLoad().
Have you tried putting the view instantiation into the loadView() function instead of the init? In any case, I would not recommend creating the view in the init function.

If you implement loadView, you must not call super. And you must make a view and assign it as self.view. Fix that and all will be well. Of course, in real life, you would never implement loadView, so all this is academic.

Related

Property 'self.navigation' not initialized at super.init call

How do I open a ViewController from #IBAction. In below #IBAction I tried to open a ViewController but I am getting Property 'self.navigation' not initialized at super.init call error:
// MARK: - IBActions
#IBAction func doAddCard() {
// OTHER CODE GOES HERE
let viewController = PaymentsStoryboard.confirmKyc()
navigation.pushViewController(viewController, animated: true)
//...
}
Full Code:
import UIKit
final class ManualCardAddView: UIView, XibInitializable, DialogKeyboardDelegate {
var completionHandler: ((CreditCard, String) -> Void)?
private unowned let navigation: UINavigationController
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
loadFromXib()
// ...
}
#IBAction func doAddCard() {
let viewController = PaymentsStoryboard.confirmKyc()
navigation.pushViewController(viewController, animated: true)
// ...
}
}
How do I resolve above issue?
Thank You!
You need to change navigation to a mutable optional property, since it only gets set after the initialisation. All immutable properties (and mutable non-optional ones as well) must be set during initialisation.
So change
private unowned let navigation: UINavigationController
to
private unowned var navigation: UINavigationController?

Subclass of NSDatePicker mouseDown event changes datePicker instance

I have instantiated and initialized a subclass of NSDatePicker in AppDelegate -> applicationDidFinishLaunching and overrode mouseDown(with event: NSEvent)in the datePicker subclass. When I press the mouse button in the datePicker and break in its overridden mouseDown func the instance is not the one I instantiated in applicationDidFinishLaunching and so is not initialized.
I've tried creating the instance at different entry points thinking it might be a timing thing but I've gotten nowhere. I'm out of ideas and feeling a little feeble. Any Help?
The datePicker:
import Cocoa
class AlarmIVDatePicker: NSDatePicker {
var viewController: ViewController!
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
return true
}
override func mouseDown(with event: NSEvent) {
let stop = 0
}
}
The ViewController:
class ViewController: NSViewController, NSWindowDelegate{
var alarmIVDatePicker: AlarmIVDatePicker!
override func viewDidLoad() {
super.viewDidLoad()
alarmIVDatePicker = AlarmIVDatePicker()
alarmIVDatePicker.viewController = self
}
I expected I could access the values I had set but the instance is not the one I created and all the values are nil
Okay. Here's what you can do to track this down. Add this code to your AlarmIVDatePicker class:
override init(frame frameRect: NSRect) {
Swift.print("AlarmIVDatePicker being called here")
super.init(frame: frameRect)
}
convenience init() {
self.init(frame: .zero)
}
required init?(coder: NSCoder) {
Swift.print("hey, you DID drop this into a storyboard or XIB file!")
super.init(coder: coder)
}
If you set breakpoints inside these init methods, you'll catch when and where they are being called where you did not expect.

Subclass of UIView doesn't invoke didSet

I have a subclass of UIView named BaseView. In subclass of BaseView I create didSet with some code. In UIViewController I init this subclass of BaseView and he doesn't invoke his didSet
BaseView code:
class BaseView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() { }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Subclass of BaseView code:
class DetailProductView: BaseView {
var product: Product? {
didSet {
productImage.image = UIImage(named: (product?.productImageName)!)
productTitle.text = product?.title
productCompositionLabel.text = product?.description
productPriceLabel.text = "₽" + product!.productPrice!.stringValue
productWeightLabel.text = product!.productWeight!.stringValue + "г."
}
}
UIViewController code:
class DetailProductController: UIViewController {
var product: Product?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let productView = DetailProductView(frame: self.view.bounds)
view.addSubview(productView)
view.layoutSubviews()
}
}
Everything is correct. You created instance of DetailProductView, but you never set any value to it’s product property. Thus didSet was never called (cause you didn’t set anything).
If you want it to be called you should set any value to this property.

Is there a way to make an Xcode template that generates a View-Controller pair?

I often use this pattern when creating UIViewController/UIView pairs. It would be nice if I could define a template in Xcode so that I could click New File -> [template] and generate a MyViewController.swift and MyView.swift like in the example below.
MyViewController.swift
class MyViewController: UIViewController {
override func loadView() {
self.view = MyView()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension MyViewController : MyViewDelegate
{
// Provide data, pop off a navigation stack, etc
}
MyView.swift
protocol MyViewDelegate : class {
}
class MyView: UIView
{
weak var delegate : MyViewDelegate?
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup()
{
// Configure views
// Assemble
}
}

Why is deinit not called until UIView is added to parent again?

I have a UIView that am adding to a UIViewController and am generally testing de-initialization to make sure I am doing things right. But when I don't set the variable in my viewController to nil and only use .removeFromSuperView() , the deinit() method in UIView won't be called until I add the UIView another time then its called. But if I use removeFromSuperView() and set the variable to nil then deinit() is called right away. Why is that?
Here's UIView() class:
class TestView: UIView {
override init(frame: CGRect) {
super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
print("init is happneing")
}
deinit {
print("de init is happneing")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here's the parent ViewController :
class MyViewController: UIViewController {
var tstview : TestView?
//adding the UIView by tapping on a button
#IBAction func addView(_ sender: UIButton) {
let test = TestView()
tstview = test
tstview?.frame = CGRect(x: 50, y: 60, width: self.view.frame.width-100, height: self.view.frame.height-200)
tstview?.backgroundColor = UIColor.white
self.view.addSubview(tstview!)
}
override func viewDidLoad() {
super.viewDidLoad()
}
//removing UIView by touching elsewhere
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
tstview?.removeFromSuperview()
// tstview = nil
}
}
deinit is called when no one is referencing the object. If you don't set tstview to nil, your MyViewController is still referencing it, thus deinit won't be called. When you call addView, the statement tstview = test finally removes the last reference to the old view, thus triggering the deinitializer.
You can read more about the concept of deinitialization in the Swift documentation.
If you want to be notified as soon as the view is detached, override willMove(toSuperview:) instead.
class TestView: UIView {
...
override func willMove(toSuperview newSuperview: UIView?) {
if newSuperview == nil {
print("removed from parent")
}
}
}