Using (UIView) classes non-programmatically - Swift - ios

So I have created a few of classes which are subclasses of UIView. Let's say I have a class called "ExampleClass". I thought that I could use that class by creating a UIView and setting its class to "ExampleClass" under the Identity-tab. However, whenever I do this I'll get an error saying;
fatal error: init(coder:) has not been implemented: file /Users/[My Name]/Desktop/xcode/[Project Name]/ExampleClass/ViewController.swift, line [n]
When I create a similar View programmatically by saying:
let MyView = ExampleClass
MyView.frame = CGRect(x: 0, y: 0, width: 100, height: 400)
I do not get this error.
The class does contain the required init, which is the cause of this error:
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

You have to call super.init(coder: aDecoder) inside yourinit` function

Related

How to render .xib (custom view) programmatically by NSClassFromString?

I am new to Swift.
Currently I have made serval xib files and they can be rendered in the following codes
let mySubview:customView = customView(frame: CGRect(x:10,y:300, width: 312, height:355))
self.view.addSubview(mySubview)
"customView" is the custom view (.xib) file while there are many others. However, I want to render it with a function parameter. I have used string but I got an error for this:
func addComp(name:String){
let className = NSClassFromString("MyApp."+name) as! UIView.Type
let subview = className.init()
subview.frame = CGRect(x:10,y:300, width: 312, height:355)
self.view.addSubview(subview)
}
It says
"Fatal error: Unexpectedly found nil while unwrapping an Optional value"
Anyway, is there any ways to define a custom view with function parameters? Either with string or any other methods.
I'd say you're coming at this wrong. Don't turn a string to a type; use a type directly.
Here's a UIView subclass with a factory method that does everything your addComp does:
class MyFactoryView : UIView {
required override init(frame:CGRect) {
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
}
static func make(andAddTo v:UIView) {
let subv = self.init(frame:CGRect(x:10,y:300, width: 312, height:355))
v.addSubview(subv)
}
}
Okay, so let's say we have some subclasses of that type:
class MyView1 : MyFactoryView {}
class MyView2 : MyFactoryView {}
So now there's no need for any string. To enact our little drama, we just talk directly to the type; for example:
MyView1.make(andAddTo:self.view)

How to add an .sks files to existing Swift/Sprite-Kit project?

I started following Ray Wenderlich's 'Space Invaders' tutorial, but have diverged considerably. I now have 3 SKScenes - my title screen, my main game screen and my end level/game over screen. The title screen and the end game scene I added and these both have .sks files; the main game screen does not and all elements (SKSpriteNodes etc) are placed programatically. The flow of my program is as follows:
I now would actually like to place some events of the main game screen via the scene editor, so I created a .sks file for it and tried to change my titleScene.swift as follows:
from:
let gameScene = GameScene(size:CGSize(width: 1536, height: 2048))
to:
let gameScene = SKScene(fileNamed: "GameScene.sks") as! GameScene!
However, this then gives:
I tried to remove the required init(coder aDecoder: NSCoder) but Xcode then complains that
required init(coder: must be supplied by subclass of SKScene
However my titleScene and gameOverScene are also sub-classes of SKScene and they don't have init(coder:)
I really can't see the difference in what I'm doing to display my titleScreen and my gameOverScene via (fileNames:) and their .sks file and trying to do the same for my gameScene.
The reason why you are getting the required is because you have variables that are not optional or not initialized before init takes place.
If you have variables that need to be assigned inside of an init function, then you would do:
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
But then you will ask me: Mr Knight0fDragon, it is telling me to replace fileNamed with coder, and it is not compiling when I switch it.
Well this is because init(fileNamed:) is a convenience init, not a designated init. In order to be able to subclass a class and get all of it's convenience inits, you need to override all of it's designated inits.
Now with SKScene, you have 3, and you already know about 1.
Let's override the other 2:
override init() {
super.init()
}
override init(size: CGSize) {
super.init(size: size)
}
Alright, now this puppy should be ready to compile, we just need to get the variables assigned.
Well what I like to do is create a setup method for any variable that has to be assigned in any version of initialization after the super is called.
Unfortunately we can't do this for constants before super is called, so those we would need to set up in each method. The reason being is that self does not fully exist yet.
This would end up looking like this:
let constant : String
required init?(coder aDecoder: NSCoder)
{
constant = "hi"
super.init(coder: aDecoder)
setup()
}
override init() {
constant = "hi"
super.init()
setup()
}
override init(size: CGSize) {
constant = "hi"
super.init(size: size)
setup()
}

Missing Argument for parameter ‘coder’ in call

I have coded a custom UIButton as :
class AccountOpeningButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
......
}
}
I am able to instantiate this Class successfully using my Storyboard.
Now, i made a UIView & want to add this button in my UIView as :
var customView:UIView = UIView()
customView.frame = CGRect(x: 0, y: 0, width: 350, height: 250)
.....
let fromDateBtn:UIButton = AccountOpeningButton()//Error comes here as : Missing Argument for parameter ‘coder’ in call
customView.addSubview(fromDateBtn)
So please help in in reusing this code dynamically also.
P.S. : I referred http://napora.org/nscoder-and-swift-initialization/
Fatal error: use of unimplemented initializer 'init(coder:)' for class
Class does not implement its superclass's required members
But didn't succeed.
=======================================================================
TRIED
let fromDateBtn:UIButton = UIButton() as! AccountOpeningButton
This throws CastException Could not cast value of type 'UIButton' to '.AccountOpeningButton'
Replace This line
let fromDateBtn:UIButton = AccountOpeningButton()
With This:
let fromDateBtn = AccountOpeningButton()
And add this method in your class
override init(frame: CGRect) {
super.init(frame: frame)
}
You can have more than one init method, but you have to obey the
inheritance and hierarchy rules. And you need to definitely understand
what are called convenience initializers.
For more details find Here

trouble with init coder function in custom view class

I'm trying to place a view from a custom view class on my main view controller programatically. Here is my code:
import UIKit
class BarControl: UIView {
// MARK: Initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
button.backgroundColor = UIColor.redColor()
button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)
addSubview(button)
}
override func intrinsicContentSize() -> CGSize {
return CGSize(width: 240, height: 44)
}
}
And in my main view controller:
let myView = BarControl()
An error comes up saying "Missing argument for parameter 'coder' in call"
What should I put in the brackets of let myView = BarControl()?
When I subclass UIView I always override init(frame: CGRect) and add any additional implementation. Then xcode usually tells you that - ‘required’: initializer ‘init(coder:)’ must be provided by subclass of ‘UIView’ and if you click that error and hit enter it automatically added that extra initializer. Well, I've been doing this without fully understanding why I have to add that extra initializer I never use. And your question finally made me look for the reason. I found a good tutorial about this initializer confusion(?) in the following link.
http://www.edwardhuynh.com/blog/2015/02/16/swift-initializer-confusion/
It's a short article about your confusion(?) =)
Basically the answer for your question is you should override init () to call BarControl() instead of init(coder aDecoder: NSCoder!). In addition, you have to add all the UIView's initializers to use the initializer you want to use according to the article.
The fonction prototype is looking like this:
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
// Your code here
}
I dont think you need the optional?

Get property from NSCoder in required init(coder aDecoder: NSCoder)

I am implementing a Circle class (subclass of UIView) in Swift that sets its radius in its initializer according to the frame that is passed in init(frame: CGRect) like so:
override init(frame: CGRect)
{
radius = frame.width/2.0
super.init(frame: frame)
}
I also want to ensure for the case when the circle is instantiated from Interface Builder, so I also implement 'required init(coder aDecoder: NSCoder)` (which I am forced to do by Xcode anyway).
How can I retrieve the frame property of the view that is somehow contained in aDecoder. What I want to achieve basically would look like this:
required init(coder aDecoder: NSCoder)
{
var theFrame = aDecoder.someHowRetrieveTheFramePropertyOfTheView // how can I achieve this?
radius = theFrame.width/2.0
super.init(coder: aDecoder)
}
You could compute the radius after the frame has been set by super.init():
required init(coder aDecoder: NSCoder)
{
radius = 0 // Must be initialized before calling super.init()
super.init(coder: aDecoder)
radius = frame.width/2.0
}
Martin's answer is the correct one. (Voted). You might be able to find the way that the base class encodes the frame value and extract it, but that is fragile. (It relies on private details of the implementation of the base class, which might change and break your app in the future.) Don't develop code that depends on non-public implementation details of another class, or of your base class. That's a future bug just waiting to happen.
The pattern in initWithCoder is to first call super to get the values for the ancestor class, then extract the values for your custom class.
When you do it that way, the ancestor class will have already set up your view's frame for you, and you can just use that.

Resources