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())
Related
My app has a hierarchy of classes for creating custom view controllers.
The first class is AppViewController. It extends NSViewController and contains methods common to all of my view controllers, like displaying alerts, retrieving data from the database, and so forth. It does not define any variables.
class AppViewController: NSViewController
{
...
}
The next class is ListViewController and is common to all of my "list" views. These are views that contain a single NSTableView with a list of all of the records from the associated database table. It extends AppViewController and conforms to the usual protocols.
Note that this class is generic so that it can properly handle the different views and data models.
class ListViewController<Model: RestModel>: AppViewController,
NSWindowDelegate,
NSTableViewDataSource,
NSTableViewDelegate
{
...
}
ListViewController defines a number of variables, including an IBOutlet for an NSTableView. That outlet is not wired to anything in the storyboard. The plan is to set it at run-time.
ListViewController also defines various functions including viewDidLoad(), viewWillAppear(), a number of app-specific functions, and so on.
The last class is specific to a database model and view, in this case, the Customers view. It extends ListViewController.
class Clv: ListViewController<CustomerMaster>
{
...
}
CustomerMaster is a concrete class that conforms to the RestModel protocol.
The problem:
The strange thing is that the last class, Clv, does not show up in the storyboard's Custom Class: Class pull-down menu, meaning that I cannot specify it as the custom class for my view.
I tried just typing it in, but that results in a run-time error
Unknown class _TtC9Inventory3Clv in Interface Builder file ...
If I remove the <Model: RestModel> from the ListViewController class definition and also remove the <CustomerMaster> from the Clv class definition, the Clv class then appears in the Class menu (of course that doesn't really help, just an observation).
AppViewController and ListViewController both do appear in that menu.
I am at a loss.
Earlier this year I created a similar architecture for an app, and I have to tell you: It can't work with storyboards, as those don't know anything about generics during instantiation.
What works is using nibs though, as you than still can init your view controller yourself.
an example:
import UIKit
class ViewController<Model: Any>: UIViewController {
var model:Model?
}
You can instantiate this view controller like
let vc = ViewController<ListItem>(nibName: "ListViewController", bundle: nil)
or subclass it
class ListViewController: ViewController<ListItem> {
}
and instantiate it like
let vc = ListViewController(nibName: "ListViewController", bundle: nil)
Now it compiles and runs, but you haven't gained much yet, as you cannot wire up your nib with generic properties.
But what you could do is to have a UIView-typed IBOutlet in a non-generic base view controller, subclass it with a generic view controller that has two generic contracts: one for the model, one for the view, ass you most likely want this to be adapted for your model. But now you must have some code that knows how to bring your model on the view. I call this renderer, but you will also find many examples were such an class is called Presenter.
The view controllers:
class BaseRenderViewController: UIViewController {
var renderer: RenderType?
#IBOutlet private weak var privateRenderView: UIView!
var renderView: UIView! {
get { return privateRenderView }
set { privateRenderView = newValue }
}
}
class RenderedContentViewController<Content, View: UIView>: BaseRenderViewController {
var contentRenderer: ContentRenderer<Content, View>? {
return renderer as? ContentRenderer<Content, View>
}
open
override func viewDidLoad() {
super.viewDidLoad()
guard let renderer = contentRenderer, let view = self.renderView as? View else {
return
}
do {
try renderer.render(on: view)
} catch (let error) {
print(error)
}
}
}
The renderers:
protocol RenderType {}
class Renderer<View: UIView>: RenderType {
func render(on view: View) throws {
throw RendererError.methodNotOverridden("\(#function) must be overridden")
}
}
class ContentRenderer<Content, View: UIView>: Renderer<View> {
init(contents: [Content]) {
self.contents = contents
}
let contents: [Content]
override func render(on view: View) throws {
throw RendererError.methodNotOverridden("\(#function) must be overridden")
}
}
You can now subclass ContentRenderer and overwrite the render method to show your content on the view.
tl;dr
By using the approach I just illustrated you can combine any generic view controller with different models, renderers and views. You gain an incredible flexibility — but you won't be able to use storyboards with it.
The answer by #vikingosegundo, while explaining Xcode's complaint and being generally very informative, didn't help me solve my particular problem. My project was started in Xcode 8.3.3 and I already have lots of windows and views in the storyboard so I don't really want to abandon or work around the storyboard/generic issue.
That being said, I did some more research and came to the realization that many people prefer delegation to class inheritance so I decided to explore that approach. I was able to get something working that satisfies my needs.
I present here, a simplified, but functional approach.
First, a protocol that our data models must conform to:
protocol RestModel
{
static var entityName: String { get }
var id: Int { get }
}
Next, a data model:
///
/// A dummy model for testing. It has two properties: an ID and a name.
///
class ModelOne: RestModel
{
static var entityName: String = "ModelOne"
var id: Int
var name: String
init(_ id: Int, _ name: String)
{
self.id = id
self.name = name
}
}
Then, a protocol to which all classes that extend our base class must conform:
///
/// Protocol: ListViewControllerDelegate
///
/// All classes that extend BaseListViewController must conform to this
/// protocol. This allows us to separate all knowledge of the actual data
/// source, record formats, etc. into a view-specific controller.
///
protocol ListViewControllerDelegate: class
{
///
/// The actual table view object. This must be defined in the extending class
/// as #IBOutlet weak var tableView: NSTableView!. The base class saves a weak
/// reference to this variable in one of its local variables and uses that
/// variable to access the actual table view object.
///
weak var tableView: NSTableView! { get }
///
/// This method must perform whatever I/O is required to load the data for the
/// table view. Loading the data is assumed to be asyncronous so the method
/// must accept a closure which must be called after the data has been loaded.
///
func loadRecords()
///
/// This method must simply return the number of rows in the data set.
///
func numberOfRows() -> Int
///
/// This method must return the text that is to be displayed in the specified
/// cell.
/// - parameters:
/// - row: The row number (as supplied in the call to tableView(tableView:viewFor:row:).
/// - col: The column identifier (from tableColumn.identifier).
/// - returns: String
///
func textForCell(row: Int, col: String) -> String
} // ListViewControllerDelegate protocol
Now the actual base class:
class BaseListViewController: NSViewController,
NSTableViewDataSource,
NSTableViewDelegate
{
//
// The instance of the extending class. Like most delegate variables in Cocoa
// applications, this variable must be set by the delegate (the extending
// class, in this case).
//
weak var delegate: ListViewControllerDelegate?
//
// The extending class' actual table view object.
//
weak var delegateTableView: NSTableView!
//
// Calls super.viewDidLoad()
// Gets a reference to the extending class' table view object.
// Sets the data source and delegate for the table view.
// Calls the delegate's loadRecords() method.
//
override func viewDidLoad()
{
super.viewDidLoad()
delegateTableView = delegate?.tableView
delegateTableView.dataSource = self
delegateTableView.delegate = self
delegate?.loadRecords()
delegateTableView.reloadData()
}
//
// This is called by the extending class' table view object to retreive the
// number of rows in the data set.
//
func numberOfRows(in tableView: NSTableView) -> Int
{
return (delegate?.numberOfRows())!
}
//
// This is called by the extending class' table view to retrieve a view cell
// for each column/row in the table. We call the delegate's textForCell(row:col:)
// method to retrieve the text and then create a view cell with that as its
// contents.
//
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
{
if let col = tableColumn?.identifier, let text = delegate?.textForCell(row: row, col: col)
{
if let cell = delegate?.tableView.make(withIdentifier: (tableColumn?.identifier)!, owner: nil) as? NSTableCellView
{
cell.textField?.stringValue = text
return cell
}
}
return nil
}
} // BaseListViewController{}
And, finally, an extending class:
///
/// A concrete example class that extends BaseListViewController{}.
/// It loadRecords() method simply uses a hard-coded list.
/// This is the class that is specified in the IB.
///
class ViewOne: BaseListViewController, ListViewControllerDelegate
{
var records: [ModelOne] = []
//
// The actual table view in our view.
//
#IBOutlet weak var tableView: NSTableView!
override func viewDidLoad()
{
super.delegate = self
super.viewDidLoad()
}
func loadRecords()
{
records =
[
ModelOne(1, "AAA"),
ModelOne(2, "BBB"),
ModelOne(3, "CCC"),
ModelOne(4, "DDD"),
]
}
func numberOfRows() -> Int
{
return records.count
}
func textForCell(row: Int, col: String) -> String
{
switch col
{
case "id":
return "\(records[row].id)"
case "name":
return records[row].name
default:
return ""
}
}
} // ViewOne{}
This is, of course, a simplified prototype. In a real-world implementation, loading the records and updating the table would happen in closures after asynchronously loading the data from a database, web service, or some such.
My full prototype defines two models and two view controllers that extend BaseListViewClass. It works as desired. The production version of the base class will contain numerous other methods (which is why a wanted it to be a base class in the first place :-)
I'm starting to learn Swift.
I've a viewController that has a var which needs updating from an outside viewController. So I added public to its declaration but my code won't compile because my class is internal (by default). So i make my class public but then it forces me to make all functions inside my class public including viewDidLoad, the tableView dataSource and delegate methods. What am i doing wrong? I don't want anyone else to call my controller's viewDidLoad.
All I wanted to viewControllerA to access a var inside viewControllerB without exposing every function inside viewControllerB to the outside world.
In ObjC, this can be achieved very easily by marking the property readonly in the headerfile and readwrite in the implementation. In this case, I would've the property in the header file so it's read-writable from outside.
here's some pseudo code
class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var myTitle: NSString?
override func viewDidLoad() {
super.viewDidLoad()
}
}
// objC part
MyViewController *myViewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
myViewController.myTitle = #""; // not available
Now if i make myTitle a public var, i get this error
Declaring a public var for an internal class
So I make MyViewController a public class.
Now i get bunch of errors
Method 'tableView(_:numberOfRowsInSection:)' must be declared public
because it matches a requirement in public protocol
'UITableViewDataSource'
You could make a protocol to save and access data across view controllers. Here's one way to do it.
// Make a custom protocol delegate with a method to store the variable. In this case I'll store a boolean.
protocol storeViewControllerBVariableDelegate {
func storeVariable(data: Bool?)
}
// In your view controller A, assign your custom protocol delegate to it and add the new delegate method.
class viewControllerA: UIViewController, storeViewControllerBVariableDelegate {
func storeVariable(data: Bool?) {
self.variableName = data
}
}
// In your view controller A's prepare for segue, assign the stored variable to view controller B if you wanted to pass it forward and backward between view controllers.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let viewControllerB = segue.destinationViewController as! viewControllerB
viewControllerB.variableName = variableName
}
// In your view controller B, initialize a variable and assign it to the delegate.
class viewControllerB: UIViewController {
var variableName: Bool!
var delegate: storeViewControllerBVariableDelegate?
// However you want to save the variable in view controller B, you can do so in an IBAction, viewDidLoad, etc.
#IBAction func saveVariable(sender: UIButton) {
delegate?.storeVariable(self.variableName)
}
}
Here are two solutions that I can think of for passing variables between view controllers
Global Option
ViewController2.swift
import UIKit
var globalVariable = String()
class ViewController1: UIViewConroller {
}
ViewController2.swift
class ViewController2: UIViewController {
overload func viewDidLoad() {
globalVariable = "some string data"
}
}
you can now access that variable globally.
Segue Option
I think a better way to handle sending data back and forth between View Controllers is by using delegates and the prepareForSegue function which is covered in depth here.
You declare your prepareForSegue function like so:
View Controller 1
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "yourIdentifierInStoryboard") {
var yourNextViewController = (segue.destinationViewController as yourNextViewControllerClass)
yourNextViewController.value = yourValue
ViewController 2
class yourNextViewControllerClass {
var value:Int! // or whatever
The you can call it programmatically
self.performSegueWithIdentifier("yourIdentifierInStoryboard", sender: self)
If you want to set values back from your second View Controller, you can use a delegate Method, to respect the original author of this content I'll redirect you to his post:
Read the rest from the original post.
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".
i will overload the init Method in Swift how i can implement that?
here my code that not work
code removed
Edit:
So it would be work fine
override init() {
super.init();
}
init(title:String?) {
super.init();
self.title = title
}
convenience init(title:String?, imageName:String?) {
self.init(title:title)
self.imageName = imageName
}
convenience init(title:String?, imageName:String?, contentKey:String?) {
self.init(title:title, imageName:imageName)
self.contentKey = contentKey
}
Updated Answer
class Y { }
class X : Y {
var title: String?
var imageName: String?
convenience override init() {
self.init(title: nil, imageName: nil)
}
convenience init(title:String?) {
self.init(title: title, imageName: nil)
}
init(title: String?, imageName: String?) {
self.title = title
self.imageName = imageName
super.init()
}
}
Use the most complete initializer as the designated initializer.
In this case init(title: String?, imageName: String?) is the only initializer that sets all its properties, so it should be the designated initializer.
Initialize your properties before calling super.init().
My old answer only worked because title and imageName were both var and optional.
In Two-Phase Initialization section of The Swift Programming Language: Initialization
Safety check 1
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
Old Answer
I plugged the sample into a playground, here is how I got it to work:
class Y { }
class X : Y {
var title: String?
var imageName: String?
override init() {
}
init(aTitle:String?) {
super.init()
self.title = aTitle
}
convenience init(aTitle:String?, aImageName:String?) {
self.init(aTitle: aTitle)
self.imageName = aImageName
}
}
init(aTitle:String?, aImageName:String?) cannot call init(aTitle:) and still be a designated initializer, it must be a convenience initializer.
self.init must be before anything else in init(aTitle:String?, aImageName:String?).
initializer must be passed the parameter name self.init(aTitle) must be self.init(aTitle: aTitle).
As a side note, I removed the unneeded semicolons and put super.init() first for style reasons.
Hope that helps.
UPDATE
To follow Apple's advice, there should only be one designated, the rest should be convenience initializers. For example, if you decide init(aTitle:String?) should be the designated initializer, then the code should look like:
convenience override init() {
self.init(aTitle: nil) // A convenience initializer calls the designated initializer.
}
init(aTitle:String?) {
super.init() // Only the designated initializer calls super.init.
self.title = aTitle
}
convenience init(aTitle:String?, aImageName:String?) {
self.init(aTitle: aTitle) // A convenience initializer calls the designated initializer.
self.imageName = aImageName
}
There are times when you might want more than once designated initializer, for example, UIView, but that should be an exception, not the rule.
UPDATE 2
Classes should have one designated initializer. Convenience initializer will (eventually) call the the designated initializer.
Initialization
A class may have multiple initializers. This occurs when the initialization data can take varied forms or where certain initializers, as a matter of convenience, supply default values. In this case, one of the initialization methods is called the designated initializer, which takes the full complement of initialization parameters.
Multiple initializers
The Designated Initializer
The initializer of a class that takes the full complement of initialization parameters is usually the designated initializer. The designated initializer of a subclass must invoke the designated initializer of its superclass by sending a message to super. The convenience (or secondary) initializers—which can include init—do not call super. Instead they call (through a message to self) the initializer in the series with the next most parameters, supplying a default value for the parameter not passed into it. The final initializer in this series is the designated initializer.
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