I have a custom UITextField and I'm trying to declare it like:
let textField = StandardTextField() // pretty much like UITextField()
My custom text field looks like:
class StandardTextField: UITextField {
init(frame: CGRect, size: CGFloat) {
super.init(frame: frame)
// some initialization
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
This is giving me an error because TextField has no initializer with no arguments. I tried adding:
init() {
super.init()
}
But this isn't possible since super.init() isn't the designated initializer. How can I achieve this?
You are probably looking for a convenience initialiser. But you will need to define a default frame size for your UITextField. Try like this:
class StandardTextField: UITextField {
convenience init() {
self.init(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
// some initialisation for init with no arguments
}
override init(frame: CGRect) {
super.init(frame: frame)
// some initialisation for init with frame
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Well unless I'm blatantly missing something, you've declared your class as class Textfield: UITextField and based on how you're trying to create your object, your class should be defined as class StandardField: UITextField and the class doesn't need any initializers as Leo said.
class StandardTextField: UITextField {
//no initializers
}
Related
I am kind of new to Swift and I can't figure this out. I have two classes where I need to use the same function to set up a custom UIStackVIew (Rating Control that shows rating stars). Each class has a variable called value that needs to be passed inside the function. I don't want to be duplicating the same code of the setUpStackView function inside each class. I have the following code:
class Class1: UIStackView {
var variable1 = "value1"
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
setUpStackView(value: variable1)
}
class Class2: UIStackView {
var variable2 = "value2"
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
setUpStackView(value: variable2)
}
extension Class1 {
func setUpStackView(value: String){
//code to set UIStackView rating control and to use the variable value
}
}
How can I implement the extension for the Class2? I am stuck with this. Any help would be greatly appreciated!
One solution can be moved common code to protocol where you can abstract out:
protocol BaseStackView {
var variable :String { get set }
}
class Class1: UIStackView,BaseStackView {
var variable = "value1"
override init(frame: CGRect){
super.init(frame: frame)
self.setUpStackView(value: variable)
}
required init(coder: NSCoder) {
super.init(coder: coder)
self.setUpStackView(value: variable)
}
}
class Class2: UIStackView,BaseStackView {
var variable = "value2"
override init(frame: CGRect){
super.init(frame: frame)
self.setUpStackView(value: variable)
}
required init(coder: NSCoder) {
super.init(coder: coder)
self.setUpStackView(value: variable)
}
}
extension UIStackView {
func setUpStackView(value: String) {
//Your setup here
}
}
You have many options.
You can make Class2 inherit from Class1:
class Class2: Class1 {
var value = "value2" //You have access to Class1's value, so you can change it here
setUpStackView(value: value) //But there's a problem here
}
But you can't just call a function when you're in the middle of a class declaration.
But you can do this in the initializer:
class Class2: Class1 {
override init(frame: CGRect) {
super.init(frame: frame)
value = "value2"
setUpStackView(value: value)
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
value = "value2"
setUpStackView(value: value)
}
}
The above is kind of jank because you're referencing class properties within its own initializer.
Here's another solution:
You can make your setup function a function extension of UIStackView:
extension UIStackView {
func setUpStackView(value: String) {
//Your setup here
self.someProperty = value //self is referring to the stackview itself
}
}
Another option is to create a static function.
extension Class1 {
static func setUpStackView(stackVw: UIStackView, value: String) {
stackVw.someProperty = value
//Doing it like this still makes this function "belong" to Class1
//It also makes it so anyone can set up their stack view like
//this because they have to pass their stack view in here
}
}
Usage would be like this regardless of what class you're in and what inheritance hierarchy you have:
override init(frame: CGRect) {
super.init(frame: frame)
Class1.setUpStackVw(stackVw: self, value: self.variable)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Class1.setUpStackVw(stackVw: self, value: self.variable)
}
Yet another option is to create a protocol from which both Class1 and Class2 conform to.
protocol StackVwWithSpecialSetUp where Self: UIStackView {
var value: String {get}
}
extension StackVwWithSpecialSetUp {
func setUpStackView()
{
self.someProperty = self.value
}
}
And then you'd have your class conform to it
class Class1Or2: UIStackView, StackVwWithSpecialSetUp {
var value: String = "blah" //Compiler will force you to implement this
override init(frame: CGRect) {
super.init(frame: frame)
//Now you can use this
setUpStackVw()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//You can use this here too
setUpStackVw()
}
}
Perhaps the way I'd do it is to just eliminate the need for the value property altogether:
class Class1: UIStackView {
override init(frame: CGRect) {
super.init(frame: frame)
setUpStackVw()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpStackVw()
}
func setUpStackVw()
{
self.accessibilityHint = "value1"
}
}
//////////
class Class2: Class1 {
override func setUpStackVw()
{
self.someProperty = "value2"
}
}
I want to draw a line programmatically in swift. I have working code for it (I think), but calling the function requires a CGRect argument. And I'm unsure what code to write there.
The draw() class and function looks like this:
class LineView : UIView {
override func draw(_ rect: CGRect) {
var aPath = UIBezierPath()
aPath.move(to: CGPoint(x:2, y:2))
aPath.addLine(to: CGPoint(x:6, y:6))
aPath.close()
}
}
Now calling it from the main ViewDidLoad it would look like this:
var line = LineView()
line.draw(MISSING ARGUMENT)
But I have no idea what argument I'm supposed to pass. Nothing of the CGRect is used in the function, so I'm not even sure of its purpose.
UPDATE
In main I create the object like this:
class ViewController: UIViewController {
#IBOutlet weak var gameBoard: UIView!
override func viewDidLoad() {
super.viewDidLoad()
var line = LineView()
gameBoard.addSubview(line)
}
}
And my draw class looks like this:
import UIKit
class LineView : UIView {
override init(frame: CGRect) {
super.init(frame: frame) //super = DO REGULAR INIT STUFF FOR UIView
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
var aPath = UIBezierPath()
aPath.move(to: CGPoint(x:2, y:2))
aPath.addLine(to: CGPoint(x:6, y:6))
aPath.close()
UIColor.red.set()
aPath.stroke()
aPath.fill()
setNeedsDisplay()
}
}
Got it working with:
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Don't call draw(rect: yourself. Never do.
It's implicitly called once by the framework after the view was initialized.
If you want to redraw the view call setNeedsDisplay() on the view.
I'm trying to write a class that is of type UIView, but on initialization I want it to take an extra parameter, but I can't figure out how to get around the UIView needing its params instead. Any help is much appreciated!
class MenuBar: UIView {
let homeController: HomeController
init(controller: HomeController){
homeController = controller
super.init()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In the ViewController I'm initializing it like this:
let menuBar: MenuBar = {
let mb = MenuBar(controller: self)
return mb
}()
Try this.
class MenuBar: UIView {
let homeController: HomeController
required init(controller: HomeController){
homeController = controller
super.init(frame: CGRect.zero)
// Can't call super.init() here because it's a convenience initializer not a desginated initializer
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
From my experience this is what works best if you want to have custom initialiser for UIView:
class CustomView : UIView {
private var customProperty: CustomClass
required init(customProperty: CustomClass) {
super.init(frame: .zero)
self.customProperty = customProperty
self.setup()
}
required override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
fileprivate func setup() {
//Here all custom code for initialisation (common for all creation methods)
}
}
This approach allows you to keep common initialisation code regardless of method of creating the view (both storyboard and code)
That's about creating UIView properly.
Additionally I would recommend to avoid passing UIViewController to UIView - I think you are trying to solve some problem in a wrong way.
Much better ways to communicate between those two is to use delegate or closure - but that's a bit off-topic - maybe you can create another question about why you want to pass it like this.
My UIView looks like this :
override init (frame : CGRect)
{
super.init(frame : frame)
}
I would like to add an argument such as :
override init (frame : CGRect, number:Int)
What is the right way to do it so I can init and send arg in one line ?
You would define your class something like this:
class MyView: UIView {
let number: Int
init(frame: CGRect, number: Int) {
self.number = number
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
// Need to initialize the number property here. Do so appropriately.
super.init(coder: aDecoder)
}
}
The use of the number property is just an example. Do what you actually need.
And you would create an instance like this:
let mv = MyView(frame: .zero, number: 42)
Obviously you would pass a useful frame.
You can do something like this:
class CustomView: UIView{
var number: Int
init(frame : CGRect, number: Int) {
self.number = number
print("Number = \(number)")
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then add it to the stack like so:
let viewToAdd = CustomView(frame: CGRect(x: 20, y: 72, width: 100 , height: 100), number: 1)
viewToAdd.backgroundColor = .red
self.view.addSubview(viewToAdd)
import UIKit
class RightAnswerButtonClass: UIButton {
var rightAnswer: Bool
init() {
super.init()
rightAnswer = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am trying to create a custom class button with the variable "rightanswer" stored as a bool. However, when I try to build, I recieve the error "Must call a designated initializer"
It's compile time error where super class designated initialisation not implement.
As subclass of UIButton must implement(override) init(frame:CGRect)("Designated Initialiser for UIButton") like as below,
> override init(frame: CGRect) {
super.init(frame: frame)
rightAnswer = true
}