How to figure out the UINavigationBarAppearance's super class designated initializers? - swift

now I'm trying to make a custom NavBarAppearance class that subclassing UINavigationBarAppearance, since I have the following same code's in my view controllers.
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .red // this varys in each vc
appearance.shadowColor = .clear // this also varys in each vc
So, I made a class for the custom UINavigationBarAppearance like below.
import UIKit
class OLNavigationBarAppearance: UINavigationBarAppearance {
override init(barAppearance: UIBarAppearance) {
super.init(barAppearance: barAppearance)
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(backgroundColor: UIColor, shadowColor: UIColor) {
super.init(barAppearance: ) // <- not sure what to put in here...
self.backgroundColor = backgroundColor
self.shadowColor = shadowColor
}
}
I want to make the custom BarAppearance class to take two parameters, backgroundColor and shadowColor, and tried calling super.init() in the custom initializer, and got the error, Must call a designated initializer of the superclass 'UIBarAppearance'.
So, my question is how to find out the designated initializer of the superclass XYZ.
I looked up the UIBarAppearance documentation and there were four initializers.
But first, how do people know which one is the "required" initializer?

Related

What to use as NSCoder to initialize a View?

Hey guys I created a new custom View Class and now I want to build a instance of it, I initialized it with the following code:
required init?(coder aCoder: NSCoder) {
super.init(coder: aCoder)
tapRecognizer = UITapGestureRecognizer(
target: self, action: #selector(handleBarTap))
addGestureRecognizer(tapRecognizer)
}
deinit {
removeGestureRecognizer(tapRecognizer)
}
And this is the instance, but what can I use as coder?
lazy var chartView = TutorialChartView(coder: )
Thanks in advance!
When you say View, do you mean UIView? The problem is that that's the wrong initializer. init(coder:) is not something you call; it's a process initiated by the storyboard when there is one of these things in the storyboard and you load that view controller.
The code UIView designated initializer is init(frame:). Implement that and call it, and you're all set.
(You may also have to provide an implementation of init(coder:) but it should just throw a fatalError, because you do not expect to be called this way.)
You should cannot use coder as initializer for creating class, use frame instead, here is your code written in frame-style initializing.
override init(frame: CGRect) {
super.init(frame: frame)
tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleBarTap))
addGestureRecognizer(tapRecognizer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Here is how you use it:
lazy var chartView = TutorialChartView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)

Any way to opt out of autoresizing permanently?

I'm writing nib-less views in which I use autolayout for all my layout logic. I find myself having to turn off autoresizing with every view I instantiate. My code is littered with a lot of these:
view.translatesAutoresizingMaskIntoConstraints
Ideally I'd like to just
extension UIView/NSView {
override var translatesAutoresizingMaskIntoConstraints: Bool = false
}
and get it over with once and for all, but extensions can't override stored properties.
Is there some other simple way to switch off autoresizing for good?
Well just a suggestion since its annoying to always set that to false, just setup a function with all the shared setups for the UIView and call it every time,
its saves time and its kinda less annoying than trying and setting the values each time,
extension UIView {
func notTranslated() {
self.translatesAutoresizingMaskIntoConstraints = false
//Add any additional code.
}
}
//Usage
let view = UIView()
view.notTranslated()
You can't override this constraints properties because the UIView maybe declared in the IB
translatesAutoresizingMaskIntoConstraints according to apple.
By default, the property is set to true for any view you programmatically create. If you add views in Interface Builder, the system automatically sets this property to false.
imagine if you could override that from an extension that would lead to some conflicts if there was other UIView's that's have the opposite value True || false, so in my opinion:
Apple did this to prevent any conflicts with the views constrains, therefore if you don't like to write it every time just wrap it up in a function.
Please if anyone have additional information, don't hesitate to contribute.
UPDATE: I found this cool answer that could also work, check out the code below.
class MyNibless: UIView {
//-----------------------------------------------------------------------------------------------------
//Constructors, Initializers, and UIView lifecycle
//-----------------------------------------------------------------------------------------------------
override init(frame: CGRect) {
super.init(frame: frame)
didLoad()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
didLoad()
}
convenience init() {
self.init(frame: CGRect.zero)
}
func didLoad() {
//Place your initialization code here
//I actually create & place constraints in here, instead of in
//updateConstraints
}
override func layoutSubviews() {
super.layoutSubviews()
//Custom manually positioning layout goes here (auto-layout pass has already run first pass)
}
override func updateConstraints() {
super.updateConstraints()
//Disable this if you are adding constraints manually
//or you're going to have a 'bad time'
//self.translatesAutoresizingMaskIntoConstraints = false
translatesAutoresizingMaskIntoConstraints = false
//Add custom constraint code here
}
}
var nibless: UIView = MyNibless()
//Usage
nibless.updateConstraints()
print(nibless.translatesAutoresizingMaskIntoConstraints) //false
So simply just create MyNibless instance as UIView and this also open big door to customizations too

Avoid boilerplate code to setup UI in viewDidLoad

i am just wondering how did you properly setup your UI in your IOS developments with Swift. Generally, I feel like I need to put a lot of statements in viewDidLoad lifecycle method of a view controller to customize UI elements. I know that I can use storyboard to help to setup those UI elements but sometimes we need to make some adjustments programmatically. Those adjustments resulting in a huge and boilerplate code in viewDidLoad. So, how do you handle this ? Did your use extensions only for the UI part ? Specific classes ? How you can clearly separate UI from logic ?
Make a custom view for it!
If you find yourself writing a lot of this kind of code:
myView.someProperty1 = someValue1
myView.someProperty2 = someValue2
myView.someProperty3 = someValue3
myView.someProperty4 = someValue4
myView.someProperty5 = someValue5
myView.addSubView(subView1)
myView.addSubView(subView2)
myView.addSubView(subView3)
...
and the values that you give the properties are all independent of the view controller, it might be time to create a custom view.
Here is an example:
Create an xib file for your view, and name it the same name as your custom view. You will be adding the subviews of your custom view and all the constraints you need here.
And then you can do something like this:
#INDesignable // add this if you want to see your view drawn on the storyboard!
class MyCustomView: UIView {
#IBOutlet var subView1: UIImageView!
#IBOutlet var subView2: UITextField!
#IBOutlet var subView3: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
let view = viewFromNibForClass()
view.frame = bounds
view.autoresizingMask = [
UIViewAutoresizing.flexibleWidth,
UIViewAutoresizing.flexibleHeight
]
addSubview(view)
// set up your view here...
// set all the properties and stuff
}
private func viewFromNibForClass() -> UIView {
let bundle = Bundle(for: MyCustomView.self)
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
return view
}
}

How to change properties of multiple UI elements

I'm trying to make a set of UILabels bold, and figured it could be done with something like a CSS class. The only way I thought of though is subclassing UILabel and adding it as a Custom Class to each label:
import UIKit
class BoldLabel: UILabel {
override internal var font: UIFont? {
get {
return UIFont.boldSystemFontOfSize(16.0)
}
}
}
But this gives an error "Getter for 'font' with Objective-C selector 'font' conflicts with getter for 'font' from superclass 'UILabel' with the same Objective-C selector". So is there a way to do it like this, or a different way to easily make a bunch of labels' fonts bold?
import UIKit
class BoldLabel: UILabel {
//This method is call when you affect this class to a UILabel in your STORYBOARD
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
self.setup()
}
//This method is call when you programatically create an instance of this UILabel class
override init(frame:CGRect) {
super.init(frame:frame)
self.setup()
}
func setup() {
//All custom properties of this label class here
self.font = UIFont.boldSystemFontOfSize(28)
}
}
Results with UILabels from storyboard :

UILabel subclass appearance in Storyboard

I have created a subclass of UILabel called MyUILabel. The only thing changed is the font and font-size. It appears as expected when I run the app. However, the in the Storyboard, the default UILabel is showed. Is there any way to make Storyboards show the font and font-size from my subclass?
MyUILabel:
public class MyUILabel : UILabel {
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.font = UIFont(name: Constants.DefaultFont, size: 30)
}
}
You could make it #IBDesignable, and then implement prepareForInterfaceBuilder:
#IBDesignable
public class MyUILabel: UILabel {
public override func awakeFromNib() {
super.awakeFromNib()
configureLabel()
}
public override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
configureLabel()
}
func configureLabel() {
font = UIFont(name: Constants.DefaultFont, size: 40)
}
}
Note, IB didn't like it when I implemented init(coder:), so I moved it into awakeFromNib.
Also note that when you make an #IBDesignable class, Apple advises that you create a separate target (e.g. "File" - "New" - "Target..." - "Cocoa Touch Framework") for this designable class. For more information, see WWDC 2014 video What’s New in Interface Builder.