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?
Related
I am currently learning iOS and one part that I find confusing is having mandatory initializations for things like UIButtons. Here is an example below.
import UIKit
class CustomButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
init(backgroundColor: UIColor, title: String) {
super.init(frame: .zero)
self.backgroundColor = backgroundColor
setTitle(title, for: .normal)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure() {
//code that configures my button
}
}
So here I am creating a custom for my app. I have noticed I needed two inits.
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
and
init(backgroundColor: UIColor, title: String) {
super.init(frame: .zero)
self.backgroundColor = backgroundColor
setTitle(title, for: .normal)
configure()
}
I always assumed the first init was to actually initialize the button. It is the equivalent of me going to apple and saying "Hey, I want to be able to create a button based off of everything that UIButton has".
However, it seems that I have to have another init if I actually want to be able to customize the button. My second init is me pretty much saying "Hey, I want to be able to create a button with certain attributes such as a background color and a title". I find this kinda weird... Why do I need a separate initializer to do this? Why do I need to set the super.init(frame: .zero)? In a way, why do I even need a super.init in my custom init? Can't I pack everything into the first init?
Just in case, I was rambling. I find it confusing that we have to use two inits. I would think that we could just use one and pack everything into it. To me these two inits feel completely different and serve no purpose for each other. Any help would be much appreciated.
Thanks!
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)
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
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
I'd like to provide my own initializer for my UICollectionViewFlowLayout subclass, for the purposes of calculating my itemSize property at runtime to scale my cells properly for different screen sizes. I understand that the simplest way to do so, is to adjust the default UICollectionViewFlowLayout from the UICollectionViewController like this:
//in UICollectionViewController
override func viewDidLoad() {
super.viewDidLoad()
let someWidth = ...
let someHeight = ...
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: someWidth, height: someHeight)
}
However, I need to subclass UICollectionViewFlowLayout for the purposes of some custom layout for my UICollectionReusableView, so I thought it would be nice to declare the itemSize property in my layout subclass.
I am trying to make my own initializer, like so:
override init() {
super.init()
itemSize = CGSizeMake(someWidth, someHeight)
}
but I am greeted with the requirement:
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
When I attempt to load this viewController, I'm greeted with the fatalError. Next, I try to simply let the super implementation deal with it:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
I recall various bits of information that is forcing my mind in a state of caution, something along the lines of: If one decides to subclassing something that requires the NSCoding things, one must do something to the variables that I added to the subclass.
Can anyone advise me on this?
-- EDIT --
Is this what you're looking for?
class HmIsThisOK: UICollectionViewFlowLayout {
var myCustomSize: CGSize = CGSizeMake(60.0, 23.5)
override var itemSize: CGSize {
get { return myCustomSize }
set { myCustomSize = newValue }
}
}
or does it just make sense to follow the usual protocol of initializing itemSize in your implementation of prepareForLayout()?
If none of that works for you, it's perfectly safe to do the required init junk and just add your code to the end of it after calling super.init(...). You're not concerned with tying your custom init to the coding/encoding process, which has to do from awakening from the Nib file (usually Main.storyboard). Because the two inits() are required, you usually factor out your custom init code into a helper method.
Finally, If you are indeed awakening this object from the Nib file, you can use an overridden awakeFromNib() function to do custom initialization, without worrying about the init() prickliness.