I wrote my own Button,Textfield, ..., classes. In the storyboard in "Custom Class" I set the class to the UIElement. This works very well.
Now I needed a toolbar that is added programatically. When I add the Toolbar in my ViewController everything is fine. But I want to create my own toolbar class like this.
class MyOwnToolbar : UIToolbar {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//never called
self.backgroundColor = UIColor.redColor()
self.tintColor = UIColor.greenColor()
self.barTintColor = UIColor.blueColor()
}
override init(frame: CGRect) {
//error: super.init isn'T called on all paths before returning from initiliazer
}
In my ViewController I try to call like this:
fromToolBar = MyOwnToolBar() //call nothing?
fromToolBar = MyOwnToolBar(frame: CGRectMake(0,0,0,0)) //doesn't work because init(frame: CGRECT) doesnt work
Old code in my ViewController that worked:
self.untilToolBar = UIToolbar(frame: CGRectMake(0,0,0,0))
untilToolBar?.backgroundColor = redColor
untilToolBar?.tintColor = greenColor
untilToolBar?.barTintColor = blueColor
So I could use my working solution, but I want to unterstand why my code isn't working. So maybe somebody have a solution or good links.
It is depending how do you create you're MyOwnToolbar if you add this in interface builder and connect class to the UI element use method initWithCoder
If you're creating your MyOwnToolbar programmatically, you should use init or initWithFrame.
Example:
class MyOwnToolbar: UIToolbar {
private func initialize() {
self.backgroundColor = UIColor.redColor()
self.tintColor = UIColor.greenColor()
self.barTintColor = UIColor.blueColor()
}
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Oleg got it right, if you use storyboard or a xib to create your view controller, then init?(coder aDecoder: NSCoder) will be called.
But you are building your view controller programmatically, so init(frame: CGRect) will be called instead of init?(coder aDecoder: NSCoder).
you should override init(frame: CGRect):
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.redColor()
self.tintColor = UIColor.greenColor()
self.barTintColor = UIColor.blueColor()
}
Related
I am trying to create a custom UILabel where the text color would be red.
Here's what I tried and none of this works:
class CustomLabel: UILabel {
override func awakeFromNib() {
UILabel.appearance().textColor = UIColor.blue
textColor = UIColor.blue
}
}
You're missing another case, a Label can be created with and without nib. Try this:
class MyCustomLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// This will call `awakeFromNib` in your code
setup()
}
private func setup() {
self.textColor = .red
}
}
I have those variables in collectionviewcell file and I want to change label background and cornerradius but that file doesnt have a viewdidload how can I do that . Thanks for helps.
UIView subclasses do not have a viewDidLoad method, instead you use the view's initializer:
class MyCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
layoutUI()
}
private func layoutUI() {
label.layer.cornerRadius = 5
label.backgroundColor = .blue
}
}
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.
When using IBDesignable, the following code is common and I end up repeating every time I create a class, is there a way to avoid this repetition?
override init(frame: CGRect) {
super.init(frame: frame)
themeProp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
themeProp()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
themeProp()
}
This is how I am currently using IBDesignable to create styles for UIButton.
import UIKit
let colorWhite = colorLiteral(red: 0.9999127984, green: 1, blue: 0.9998814464, alpha: 1)
let colorLavender = colorLiteral(red: 0.6604440808, green: 0.5388858914, blue: 0.8827161193, alpha: 1)
#IBDesignable class PrimaryButtonA: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
themeProp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
themeProp()
}
func themeProp() {
setTitleColor(colorWhite, for:.normal)
self.layer.cornerRadius = 10
backgroundColor = colorLavender
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
themeProp()
}
}
#IBDesignable class PrimaryButtonB: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
themeProp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
themeProp()
}
func themeProp() {
setTitleColor(colorWhite, for:.normal)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
themeProp()
}
}
With my limited knowledge, I tried to create a function and tried calling it inside each class but it doesn't work.
It doesn't make any sense to repeat this 12 lines of code in every class declaration. So, if there is a way to avoid this repetition then please use my code for the answer.
Thanks!
A possible solution is creating a common superclass for these views. The only disadvantage is that you have to create a superclass for each type (UIViews, UIButtons etc.)
class DesignableView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
themeProp()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
themeProp()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
themeProp()
}
func themeProp() { }
}
After that, make your designable class a subclass of DesignableView. You will only have to override themeProp() in that.
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.