UIView inheritance and custom initializers throws EXC_BAD_ACCESS in swift - xcode

I have classes with inheritance like:
UIView -> TestSubclass -> Test
class Test has some properties and methods. It works until I add some initializers into class TestSubclass:
class TestSubclass : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
print("CustomActivityInit")
}
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
print("CustomActivityInitCoder")
}
}
After this I can't access properties of my Test class
override func viewDidLoad() {
super.viewDidLoad()
var test = Test(frame: CGRect.nullRect)
test.test = "zzz"
}
and get EXC_BAD_ACCESS error on line test.test = "zzz"
Could you help me understand the reason, please?
This is the test project to see the issue https://www.dropbox.com/s/1d8fvxm0es9b5n4/TestInit.zip
I use XCode 6 beta 5, deployment target iOS7+, iOS8 SDK

You must override init() because of inheritance from UIView.
class TestSubclass : UIView {
override init() {
super.init()
}
}
The reference from Apple:
Default Initializers
Swift provides a default initializer for any structure or base class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

Related

Create default custom class in Xcode

Every time I create a new class in Xcode, I would like it to have some personal structure.
Is it possible to create a default option, so every time I open the new file window, I will see my own class option ?
For example,I will have there : myClass.Swift , which will look like this :
import UIKit
class customView: UIView {
override init (frame : CGRect)
{
super.init(frame : frame)
//some view
//some vars
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
}
If not, is there any other option to get a similar effect ?

Ambiguous use of "init" with optional arguments in Swift 2

I've just updated my code from Swift 1.2 to Swift 2.1. The project was fully-functioning with previous version of Swift, but now I'm seeing "Ambiguous use of 'init'" errors. Each occurrence of this error seems to be caused by the use of optional arguments in the constructor. I've even managed to reproduce this issue in Xcode Playground with the following simplified code using the same pattern:
class Item : UIView {
override init(frame: CGRect = CGRectZero) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
let i = Item() # Ambiguous use of 'init(frame:)'
However, I still don't understand why Swift 2.1 now has a problem with this pattern, or what the alternative should be. I've tried searching for this, but all I've stumbled upon was "ambiguous use" errors for other (non-constructor) methods due to method renaming or signature changing, neither of which is the case here.
It's ambiguous use because when u call let i = Item(), there are 2 options -
the init(frame: CGRect = CGRectZero) and the init()
it's better to do something like this:
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init() {
self.init(frame: CGRectZero)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

how do I switch between NSViewControllers using NSPageController?

I have had bad luck finding any examples on the web that closely match what I am trying to do. I am trying to using NSPageController to view and switch between multiple NSPageControllers. My steps.
I create a new OS X swift project
I add an object to the ViewController and make it of NSPageController class.
I add two buttons, one I label "Next" and the other one I label "Back" for the transitions.
I link the buttons to the NSPageController object as navigateForward and navigateBack actions.
I create an outlet in the custom NSViewController class for the NSPageController object and add the specific NSPageController delegate methods.
I add two additional view controllers in storyboard and create an identifier for them to reference back in my custom view controller class: Wizard1, Wizard2.
import Cocoa
class ViewController: NSViewController, NSPageControllerDelegate {
#IBOutlet var myPageController: NSPageController!
override func viewDidLoad() {
super.viewDidLoad()
let vc1: AnyObject? = self.storyboard!.instantiateControllerWithIdentifier("Wizard1")
let vc2: AnyObject? = self.storyboard!.instantiateControllerWithIdentifier("Wizard2")
self.myPageController.arrangedObjects.append(vc1!)
self.myPageController.arrangedObjects.append(vc2!)
// Do any additional setup after loading the view.
}
override init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
myPageController = NSPageController()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil?)
}
required init?(coder aDecoder: NSCoder) {
myPageController = NSPageController()
super.init(coder:aDecoder)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func pageController(pageController: NSPageController, identifierForObject object: AnyObject!) -> String! {
return "View"
}
func pageController(pageController: NSPageController, viewControllerForIdentifier identifier: String!) -> NSViewController! {
let vc1: AnyObject? = self.storyboard!.instantiateControllerWithIdentifier("Wizard1")
return vc1 as NSViewController
}
func pageController(pageController: NSPageController, prepareViewController viewController: NSViewController!, withObject object: AnyObject!) {
viewController.representedObject = object
}
func pageControllerDidEndLiveTransition(pageController: NSPageController) {
pageController.completeTransition()
}
func pageControllerWillStartLiveTransition(pageController: NSPageController) {
self.presentViewControllerAsModalWindow(self.storyboard?.instantiateControllerWithIdentifier("Wizard2") as NSViewController)
}
}
The error I get when pressing the Next button is:
-[NSNib initWithNibNamed:bundle:] could not load the nibName: NSPageController in bundle (null).
Perhaps you are trying to load a nib with the wrong name in AppDelegate.m or wherever you are initializing your page controller.
Otherwise you have missed creating a .xib file and to name it NSPageController. When creating a Cocoa Touch Class there is a checkbox to also create an xib file for your class if needed.
This line is responsible for the error:
myPageController = NSPageController()
You're trying to initialize a view controller without a nib, that's why it does not work. By default the NSViewController's name is taken to identify the nib that corresponds to it. In your case it is "NSPageController".

Xcode 6.2 beta 2 WKInterfaceController watchkit error

This code:
class InterfaceController: WKInterfaceController {
#IBOutlet weak var petTable: WKInterfaceTable!
var petnames = ["Luna", "dylan", "Mery", "Mady", "Paul Newman", "heidi"]
override init(context: AnyObject?) {
// Initialize variables here.
super.init(context: context)
return two errors:
" Initializer does not override a designated initializer from its superclass" in the line override
" Must call a designated initializer of the superclass 'WKInterfaceController'" in the line super.init
the error is in Xcode 6.2 beta 2 (launch today)
In the previous version of Xcode no errors show
Replace
override init(context: AnyObject?) {
// Initialize variables here.
super.init(context: context)
// Configure interface objects here.
NSLog("%# init", self)
}
with
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Configure interface objects here.
NSLog("%# awakeWithContext", self)
}
Clean your project then build and run
init(context: AnyObject?) does not exist anymore. For initialization just use init and then use awakeWithContext to initialize from context.

Subclassing NSWindowController in Swift and init(windowNibName)

I am trying to start a new document based Cocoa project in Swift and want to create a subclass of NSWindowController (as recommended in Apple's guides on document based apps). In ObjC you would make an instance of an NSWindowController subclass sending the initWithWindowNibName: message, which was implemented accordingly, calling the superclasses method.
In Swift init(windowNibName) is only available as an convenience initializer, the designated initializer of NSWindowController is init(window) which obviously wants me to pass in a window.
I cannot call super.init(windowNibName) from my subclass, because it is not the designated initializer, so I obviously have to implement convenience init(windowNibName), which in turn needs to call self.init(window). But if all I have is my nib file, how do I access the nib file's window to send to that initializer?
Instead of overriding any of the init methods you can simply override the windowNibName property and return a hardcoded string. This allows you to call the basic vanilla init method to create the window controller.
class WindowController: NSWindowController {
override var windowNibName: String! {
return "NameOfNib"
}
}
let windowController = WindowController()
I prefer this over calling let windowController = WindowController(windowNibName: "NameOfNib") as the name of the nib is an implementation detail that should be fully encapsulated within the window controller class and never exposed outside (and it's just plain easier to call WindowController()).
If you want to add additional parameters to the init method do the following:
In your custom init method call super.init(window: nil). This will get NSWindowController to init with the windowNibName property.
Override the required init(coder: NSCoder) method to either configure your object or simply call fatalError() to prohibit its use (albiet at runtime).
class WindowController: NSWindowController {
var test: Bool
override var windowNibName: String! {
return "NameOfNib"
}
init(test: Bool) {
self.test = test
super.init(window: nil) // Call this to get NSWindowController to init with the windowNibName property
}
// Override this as required per the class spec
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented. Use init()")
// OR
self.test = false
super.init(coder: coder)
}
}
let windowController = WindowController(test: true)
You need to override either all three designated initializers of NSWindowController (init(), init(window) and init(coder)), or none of them, in which case your subclass will automatically inherit init(windowNibName) and all others convenience initializers and you will be able to construct it using superclass's convenience initializer:
// this overrides none of designated initializers
class MyWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
}
}
// this one overrides all of them
//
// Awkwardly enough, I see only two initializers
// when viewing `NSWindowController` source from Xcode,
// but I have to also override `init()` to make these rules apply.
// Seems like a bug.
class MyWindowController: NSWindowController
{
init()
{
super.init()
}
init(window: NSWindow!)
{
super.init(window: window)
}
init(coder: NSCoder!)
{
super.init(coder: coder)
}
override func windowDidLoad() {
super.windowDidLoad()
}
}
// this will work with either of the above
let mwc: MyWindowController! = MyWindowController(windowNibName: "MyWindow")
This is covered by "Initialization / Automatic Initializer Inheritance" in the language guide:
However, superclass initializers are automatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.
Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:
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.
I was able to work around this by just having a class method that calls the convenience initializer, modifies the custom variables, then returns the new object.
So an Objective C init method would look like this:
//Class.h
#class PPPlugInInfo;
#interface PPPlugInInfoController : NSWindowController
//...
- (id)initWithPlugInInfo:(PPPlugInInfo *)plugInfo;
//...
#end
//Class.m
#include "Class.h"
#interface PPPlugInInfoController ()
#property (strong) PPPlugInInfo *info;
#end
#implementation PPPlugInInfoController
- (id)initWithPlugInInfo:(PPPlugInInfo *)plugInfo;
{
if (self = [self initWithWindowNibName:#"PPPlugInInfoController"]) {
self.info = plugInfo;
}
return self;
}
#end
This is how I did the Swift version:
class PPPluginInfoController: NSWindowController {
private var info: PPPlugInInfo!
class func windowControllerFromInfo(plugInfo: PPPlugInInfo) -> Self {
var toRet = self(windowNibName:"PPPlugInInfoController")
toRet.info = plugInfo
return toRet
}
}
The stroke of genius in #hamstergene's answer is to override init() as well, which is inherited from NSResponder. Now one can introduce a new initialiser and delegate to self.init(windowNibName: NoteWindowName), which is in turn inherited once all three designated initialisers are overridden:
class WindowController: NSWindowController {
var note: Document! // must be optional because self is not available before delegating to designated init
convenience init(note: Document) {
self.init(windowNibName: NoteWindowName)
self.document = document
}
override init(window: NSWindow?) {
super.init(window: window)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init() {
fatalError("init() has not been implemented")
}
}
Now it is no longer necessary to tell the custom window controller what nib file to load from. Instead, it can be specialised for whatever motivated the subclass in the first place (like taking part in some document hierarchy, for example)...
An update to hamstergene answer.
This works fine on Xcode Version 6.1 (6A1052d)
//
// MainWindowController.swift
// VHDA Editor
//
// Created by Holyfield on 20/11/14.
// Copyright (c) 2014 Holyfield. All rights reserved.
//
import Cocoa
class MainWindowController: NSWindowController {
//override func windowDidLoad() {
// super.windowDidLoad()
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
// }
override init()
{
super.init()
println(__FILE__, __FUNCTION__)
}
override init(window: NSWindow!)
{
super.init(window: window)
println(__FILE__, __FUNCTION__)
}
required init?(coder: (NSCoder!))
{
super.init(coder: coder)
println(__FILE__, __FUNCTION__)
}
override func windowDidLoad() {
super.windowDidLoad()
println(__FILE__, __FUNCTION__)
}
}
Console output:
(…/MainWindowController.swift, init(coder:))
(…/MainWindowController.swift, windowDidLoad())

Resources