Trouble creating an instance of ViewController - ios

All,
I have a bar button item on my ViewController. I have set a computed property to will turn the BarButton off. I want to be able to set this from another class.
Here is my code in the view controller :
class ViewController: UIViewController {
var PayButton : Int {
didSet {
navigationItem.leftBarButtonItem = nil
}
}
required init(coder aDecoder: NSCoder)
{
self.PayButton = 0
super.init(coder: aDecoder)
}
When it try and create an instance on the view controller (so I can set the PayButton integer)
let test = ViewController()
I get an error saying
Missing Argument for parameter 'coder' in call
Any ideas ?

It is asking for the parameter 'coder', because you have it in the required init.
To use your code as it stands, you would need to initialise with:
let test = ViewController(coder: NSCoder)
There are several ways to get around this. The easiest would be to remove the required initialiser.
import UIKit
class ViewController: UIViewController {
var PayButton : Int = 0 {
didSet {
navigationItem.leftBarButtonItem = nil
}
}
}
and then implement with
let test = ViewController()
test.PayButton = 0

Because you have implemented the required initializer in ViewController class.
There are two solutions
Add a default initializer
init() {
super.init(nibName: nil, bundle:nil)
}
Remove the required initializer.
class ViewController: UIViewController {
var PayButton : Int {
didSet {
navigationItem.leftBarButtonItem = nil
}
}
init() {
self.PayButton = 0
super.init(nibName: nil, bundle:nil)
}
required init(coder aDecoder: NSCoder)
{
self.PayButton = 0
super.init(coder: aDecoder)
}

Try this:
This is the required initializer:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
This is the super initializer:
override init(frame: CGRect) {
super.init(frame: frame)
}
This is your convenience initializer where you can pass the size the view you want to create
convenience init(view: UIView){
self.init(frame: view.frame)
}
This is your convenience initializer where the view is initialized with a value pre defined:
convenience init(){
self.init(frame: CGRectZero) //Put you predefined value here
}

Related

Init a property in a class with an override init and a required init? [duplicate]

I tried to add a double value to a subclass of UIButton in Swift. I tried all kind of inits and get and set options, but I couldn’t get it to work.
So I started with this:
class CVSTButton : UIButton {
var cvstPosition: Double
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
}
}
Then I tried:
class CVSTButton : UIButton {
var cvstPosition: Double {
get {
return self.cvstPosition
}
set {
self.cvstPosition = newValue
}
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)
}
}
What’s is wrong here?
With Swift 3, according to your needs, you may choose one of the seven following code snippets to solve your problem.
1. Create your UIButton subclass with a custom initializer
This solution allows you to create instances of your UIButton subclass with the appropriate value for your property. With this solution, you can only create instances of your UIButton subclass programmatically.
import UIKit
class CustomButton: UIButton {
var myValue: Int
required init(value: Int = 0) {
// set myValue before super.init is called
self.myValue = value
super.init(frame: .zero)
// set other operations after super.init, if required
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Usage:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = CustomButton(value: 0)
// let button = CustomButton() // also works
button.setTitle("Hello", for: .normal)
// auto layout
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
print(button.myValue) // prints 0
}
}
2. Create your UIButton subclass with a convenience initializer
This solution allows you to create instances of your UIButton subclass with the appropriate value for your property. With this solution, you can only create instances of your UIButton subclass programmatically.
import UIKit
class CustomButton: UIButton {
var myValue: Int
convenience init(squareOf value: Int) {
self.init(value: value * value)
}
required init(value: Int = 0) {
// set myValue before super.init is called
self.myValue = value
super.init(frame: .zero)
// set other operations after super.init, if required
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Usage:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = CustomButton(squareOf: 10)
// let button = CustomButton(value: 100) // also works
button.setTitle("Hello", for: .normal)
// auto layout
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
print(button.myValue) // prints 100
}
}
3. Create your UIButton subclass with init(frame: CGRect) initializer
With this solution, you can only create instances of your UIButton subclass programmatically.
import UIKit
class CustomButton: UIButton {
var myValue: Int
override init(frame: CGRect) {
// set myValue before super.init is called
self.myValue = 0
super.init(frame: frame)
// set other operations after super.init, if required
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Usage:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = CustomButton(frame: .zero)
//let button = CustomButton() // also works
button.setTitle("Hello", for: .normal)
// auto layout
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
print(button.myValue) // prints 0
}
}
4. Create your UIButton subclass with init?(coder aDecoder: NSCoder) initializer
With this solution, you can create instances of your UIButton subclass from Storyboard.
import UIKit
class CustomButton: UIButton {
var myValue: Int
required init?(coder aDecoder: NSCoder) {
// set myValue before super.init is called
self.myValue = 0
super.init(coder: aDecoder)
// set other operations after super.init, if required
backgroundColor = .red
}
}
Usage:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: CustomButton!
override func viewDidLoad() {
super.viewDidLoad()
print(button.myValue) // prints 0
}
}
5. Create your UIButton subclass with init(frame: CGRect) and init?(coder aDecoder: NSCoder) initializers
With this solution, you can create instances of your UIButton subclass programmatically or from Storyboard.
import UIKit
class CustomButton: UIButton {
var myValue: Int
override init(frame: CGRect) {
// set myValue before super.init is called
self.myValue = 0
super.init(frame: frame)
// set other operations after super.init, if required
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
// set myValue before super.init is called
self.myValue = 0
super.init(coder: aDecoder)
// set other operations after super.init if required
backgroundColor = .red
}
}
6. Create your UIButton subclass with a default property value for your property
As an alternative to the previous solutions, you can assign an initial value to your property outside of the initializers.
import UIKit
class CustomButton: UIButton {
var myValue: Int = 0
override init(frame: CGRect) {
super.init(frame: frame)
// set other operations after super.init, if required
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// set other operations after super.init if required
backgroundColor = .red
}
}
7. Create your UIButton subclass with your property having an optional type
If you don't want to / can't set a default value to your property when your button is created, you must set your property type as an optional.
import UIKit
class CustomButton: UIButton {
var myValue: Int? = nil
override init(frame: CGRect) {
super.init(frame: frame)
// set other operations after super.init, if required
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// set other operations after super.init if required
backgroundColor = .red
}
}
You need two things there -- (1) cvstPosition needs an initial value, either in the declaration or in the init before you call super.init(). (2) That call to fatalError is put in so you don't forget to implement the initializer -- it’s basically an on-purpose crash. Delete!
Setting the initial value in the declaration, there isn’t any need for an init:
class CVSTButton : UIButton {
var cvstPosition: Double = 0
}
Or setting the initial value in the initializer:
class CVSTButton : UIButton {
var cvstPosition: Double
required init(coder aDecoder: NSCoder) {
cvstPosition = 0
super.init(coder: aDecoder)
}
}
Swift >= 2.2:
Since this version subclassing the UIButton, makes your button to have .custom type.
Swift 2:
convenience init(type buttonType: UIButtonType) {
super.init(frame: CGRectZero)
// this button be automatically .Custom
}
Swift:
override class func buttonWithType(buttonType: UIButtonType) -> AnyObject {
let button = super.buttonWithType(buttonType) as! UIButton
// your default code
return button
}
Note: I'm using Swift 3 in Xcode 8.3.3
This is a simple and easy workaround I've been using when needing to add custom properties and methods to a UIButton:
class CVSTButton: UIButton {
var cvstPosition: Double
static func button(withCVSTPosition cvstPosition: Double) -> CVSTButton {
let button = CVSTButton(type: .detailDisclosure) // You may adjust the initializer used to suit your needs.
button.cvstPosition = cvstPosition // Then you can simply set the the properties (which are passed as arguments to the factor/class method)
return button
}
}
To use it:
let cvstButton = CVSTButton.button(withCVSTPosition: 2.0)

Inherit properties of a UITextfield

I am new to Swift programming and I have a few textfields with similar properties. Can I create one textfield with defined properties and then extend them to other textfields. num inherits properties like borderColor and borderWidth from textfield1.
class TextElement: UITextField {
var textfiedl1: UITextField = UITextField(frame:CGRectMake(38,383,299,44))
textfield1.layer.borderColor = UIColor.grayColor().CGColor
textfield1.layer.borderWidth = 1.0
}
class TextElement2 : UITextField {
var num: TextElement = TextElement(frame: CGRectMake(38,416,299,44))
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
num.backgroundColor = UIColor.redColor()
self.addSubview(num)
}
This does not give any output. The simulator screen is blank. Any solution for this.
This is completely off the top of my head (not tested) but I think you want something along the lines of the following:
Base Class:
class BaseTextField: UITextField {
init() {
super.init(frame: CGRectZero)
self.layer.borderColor = UIColor.grayColor().CGColor
self.layer.borderWidth = 1.0
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Sub Class:
class YourTextField: BaseTextField {
init(frame: CGRect, backgroundColor: UIColor) {
super.init()
self.frame = frame
self.backgroundColor = backgroundColor
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Usage in Viewcontroller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let textField = YourTextField(frame: CGRectMake(38, 416, 299, 44), backgroundColor: UIColor.redColor())
self.view.addSubview(textField)
}
}
Note:
You probably want to actually do something in required init?(coder aDecoder: NSCoder) but for here I left the default implementation.
What you need here is a helper object. Lightweight structs are perfect for this. Create a struct embodying the configuration features. Give the struct a method that you can call, passing self as a parameter, allowing the struct to configure your text field (warning: this is Swift 3):
struct TFConfig {
let borderColor = UIColor.gray().cgColor
let borderWidth:CGFloat = 1.0
func configure(_ tf:UITextField) {
tf.layer.borderColor = borderColor
tf.layer.borderWidth = borderWidth
}
}
class MyTextField : UITextField {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
TFConfig().configure(self)
}
}
// ... similarly for other text fields

Reusable code for classes

Several classes in my app do the same thing and have the same instance variables:
// one of the many classes I have
// they all load nibs and update the frames of their views
class HelpView: UIView {
#IBOutlet var view: UIView!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
NSBundle.mainBundle().loadNibNamed("HelpView", owner: self, options: nil)
self.addSubview(self.view)
self.view.frame = self.bounds
}
}
I want to avoid duplicated code, so I thought about using a superclass so that all the classes inherit from it.
// my new superclass all classes will inherit from
class ReusableView: UIView {
#IBOutlet var view: UIView! // all subclasses have different views
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
// every class has a different nib name
NSBundle.mainBundle().loadNibNamed("Nib name goes here", owner: self, options: nil)
self.addSubview(self.view)
self.view.frame = self.bounds
}
}
The problem is that view is nil until the nib is loaded, so it's apparently not possible to call that superclass' method because you're passing a nil object. How can I handle this?
This will work:
class ReusableView: UIView {
func getHelperView() -> UIView! {
preconditionFailure("This method must be overridden")
}
func getNibName() -> String {
preconditionFailure("This method must be overridden")
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
// every class has a different nib name
NSBundle.mainBundle().loadNibNamed(self.getNibName(), owner: self, options: nil)
self.addSubview(self.getHelperView())
self.getHelperView().frame = self.bounds
}
}
class HelpView: ReusableView {
#IBOutlet var view: UIView!
override func getHelperView() -> UIView! {
return view;
}
override func getNibName() -> String {
return "NibName";
}
}

Push ViewController with coder aDecoder: NSCoder

I'm trying to push a view controller using:
var vc2 = ViewController2()
self.navigationController?.pushViewController(vc2, animated: false)
but in my second view controller, I'm have:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
But I get the error Missing argument for parameter 'coder' in call in the first ViewController. What goes in the parenthesis in the first view controller?
There are two ways you can fix this issue:
The easy way, just call the function passing nil to the parameters:
var vc2 = ViewController2(nibName: nil, bundle: nil)
The best way, create convenience initializers in your class:
class ViewController2: UIViewController {
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience override init () {
self.init(frame:CGRectZero)
}
}
and now you can call:
var vc2 = ViewController2()
class ViewController2: UIViewController {
convenience init () {
self.init(nibName: nil, bundle: nil)
}
}
Now you can call ViewController2()

IBOutlet properties nil after custom view loaded from xib

Something strange going on with IBOutlets.
In code I've try to access to this properties, but they are nil. Code:
class CustomKeyboard: UIView {
#IBOutlet var aButt: UIButton!
#IBOutlet var oButt: UIButton!
class func keyboard() -> UIView {
let nib = UINib(nibName: "CustomKeyboard", bundle: nil)
return nib.instantiateWithOwner(self, options: nil).first as UIView
}
override init() {
super.init()
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
// MARK: - Private
private func commonInit() {
println(aButt)
// aButt is nil
aButt = self.viewWithTag(1) as UIButton
println(aButt)
// aButt is not nil
}
}
That's expected, because the IBOutlet(s) are not assigned by the time the initializer is called.
Instead of calling commonInit() in init(coder:), do that in an override of awakeFromNib as follows:
// ...
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
commonInit()
}
// ...
Assuming you tried the standard troubleshooting steps for connecting IBOutlets, try this:
Apparently, you need to disable awake from nib in certain runtime cases.
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
guard subviews.isEmpty else { return self }
return Bundle.main.loadNibNamed("MainNavbar", owner: nil, options: nil)?.first
}
Your nib may not be connected. My solution is quite simple. Somewhere in your project (I create a class called UIViewExtension.swift), add an extension of UIView with this handy connectNibUI method.
extension UIView {
func connectNibUI() {
let nib = UINib(nibName: String(describing: type(of: self)), bundle: nil).instantiate(withOwner: self, options: nil)
let nibView = nib.first as! UIView
nibView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(nibView)
//I am using SnapKit cocoapod for this method, to update constraints. You can use NSLayoutConstraints if you prefer.
nibView.snp.makeConstraints { (make) -> Void in
make.edges.equalTo(self)
}
}
}
Now you can call this method on any view, in your init method, do this:
override init(frame: CGRect) {
super.init(frame: frame)
connectNibUI()
}
Building on #ScottyBlades, I made this subclass:
class UIViewXib: UIView {
// I'm finding this necessary when I name a Xib-based UIView in IB. Otherwise, the IBOutlets are not loaded in awakeFromNib.
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
guard subviews.isEmpty else { return self }
return Bundle.main.loadNibNamed(typeName(self), owner: nil, options: nil)?.first
}
}
func typeName(_ some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}
There is possibility that you not mentioned the FileOwner for xib.
Mention its class in File owner not in views Identity Inspector .
And how did you initiate your view from the controlller? Like this:
var view = CustomKeyboard.keyboard()
self.view.addSubview(view)

Resources