How to initialize an empty object of a custom UIImageView subclass - swift

I'm having a custom UIImageView subclass which runs some basic code like creating a shadow etc.
When I don't override any init everything works fine. I can create an empty CustomImageView() the same way I would create am empty UIImageView() . The moment I'm adding all the required overrides I cannot create an empty object anymore.
The error I am getting is:
Cannot invoke initializer for type 'ProfilePictureImageView' with no
arguments
with the suggestion:
Overloads for 'ProfilePictureImageView' exist with these partially matching parameter lists: (coder: NSCoder), (frame: CGRect), (image:
UIImage?)
I create the empty object in my view controller like this:
var customImageView = CustomImageView()
And my custom UIImageView subclass looks like this:
class CustomImageView: UIImageView {
override init(image: UIImage?) {
super.init(image: image)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
}
// Followed by my custom variables and methods.
}
I've tried to add the following, assuming I'm missing an empty init of some sort. But this didn't work either.
override init() {
super.init()
}

You've to make a convenience init method.
class CustomImageView: UIImageView {
//...
convenience init() {
self.init(frame: .zero) // or self.init(image: nil)
}
}

Related

UIView IBOutlet not changing when change called from a different class

I have an XIB with a UILabel to it. I have referenced the UILabel to a UIView class that I have created. I can change the label using label.text = "hi" when initializing the view. When I try and call a change from another class it doesn't change the UILabel on screen (but if I print label.text it shows as what I set it to). I cannot make the UILabel load the text when initializing as the text could be changed by the user at any time. (switchText() is called from a UITableCell)
2nd class
class Second {
func switchText() {
let first = First()
DispatchQueue.main.async {
first.label.text = "bye"
}
}
}
1st class
class First: UIView {
let kCONTENT_XIB_NAME = "First"
#IBOutlet var label: UILabel!
#IBOutlet var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
Bundle.main.loadNibNamed(kCONTENT_XIB_NAME, owner: self, options: nil)
contentView.fixInView(self)
}
override func awakeFromNib() {
super.awakeFromNib()
}
}
Also, in my XIB I have my UIView hooked up to File's Owner and contentView inside my UIView class. My label outlet goes to file's owner and then to the UIView class where it is declared as label.
You are not really changing the text on your First() class. What your switchText() function does is create another reference of the class named First and then set the text of the label for that new reference.
let first = First()
DispatchQueue.main.async {
first.label.text = "bye"
}
What you can do is make your switchText() function conform to a protocol then call it on your First() class through a delegate.
protocol SecondClassDelegate {
func didSwitchText(editedText: String)
}
class Second {
var delegate: SecondClassDelegate!
func switchText() {
delegate.didSwitchText("bye")
}
}
Now you can add this to your First() class
class First: SecondClassDelegate {
func didSwitchText(editedText: String) {
label.text = editedText
}
}
Just don't forget to set the delegate wherever you're setting your Second() class
let second = Second()
second.delegate = self
I suggest reading about this for a better understanding of delegates. https://www.appcoda.com/swift-delegate/

What argument do I use when calling draw() function?

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.

Must call a designated initializer error

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
}

Can I subclass UIView more concisely in Swift?

When subclassing UIView in Swift, you are required to have init functions
override init(frame: CGRect)
{
super.init(frame:frame)
initMyStuff()
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder:aDecoder)
initMyStuff()
}
override func awakeFromNib()
{
super.awakeFromNib()
initMyStuff()
}
internal func initMyStuff()
{
// Init class variables and setup constraints here
}
Surely there is a better way to do this. At the very least, what would a better naming convention be for that initMyStuff function? Maybe there is a way extend UIView to have a single custom initializer function I can override in my custom classes?
You can create a convenience initializer like so :)
convenience init(someValue: String) {
self.init(frame : CGRectZero)
.....
}

Custom UITextField, init with no arguments

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
}