Calling awakeFromNib() in prepareForInterfaceBuilder() - ios

I am subclassing UIButton and want my custom button to appear in Storyboard. I made all my changes in awakeFromNib() (called its super) and added #IBDesignable.
override func awakeFromNib(){
super.awakeFromNib()
... // customization stuff
}
And in prepareForInterfaceBuilder() i need to use what i did in awakeFromNib(), so if i call awakeFromNib() directly, will it cause any bad stuff to happen? If yes, what will it cause? Or it is totally fine? Like this:
override func prepareForInterfaceBuilder(){
...
self.awakeFromNib()
}
Or do i have to create a setup() function with all my customization inside, and then call it from both awake and prepare functions?
Thanks.

You should combine code in a method (as you mentioned setup() let's say) and call it in both awakeFromNib() and prepareForInterfaceBuilder() instead of calling awakeFromNib() manually.
awakeFromNib is one of the view life cycle methods, meaning that it should get called by the system at a particular status. Avoid call such a method by yourself; What you should do is to provide the needed functionality to be performed when awakeFromNib get called but not calling it by yourself.
in Apple documentation about awakeFromNib() it says "Although the
default implementation of this method does nothing, many UIKit classes
provide non-empty implementations.". So if it does nothing, what can
calling it cause?
First, you should take a look for the second part of the sentence:
many UIKit classes provide non-empty implementations.
Which means that there is a specific code should get run (in super. awakeFromNib):
You must call the super implementation of awakeFromNib to give parent
classes the opportunity to perform any additional initialization they
require.
Second, even if there is no harm from doing it, you should not do it, you should not follow the approach of calling a method that gets called by the system.

Related

viewWillAppear alternative for custom UILabel, UIButton classes

Is there any function that is similar to viewWillAppear() for UIIbutton classes?
All these functions below are called only once
prepareForInterfaceBuilder()
awakeFromNib()
init()
So not exactly like a viewWillAppear, No.
This is because these are fundamentally different aspects of the architecture. One is usually the manager(a viewController), and the other always is a minion(UIView).
Their lifecycles are naturally different.
But if you want to perform a change in your custom view, whether a UIButton or any other UIView subclass, what you can do to reset is to use the method setNeedsLayout().
From the apple docs
Call this method on your application’s main thread when you want to
adjust the layout of a view’s subviews. This method makes a note of
the request and returns immediately. Because this method does not
force an immediate update, but instead waits for the next update
cycle, you can use it to invalidate the layout of multiple views
before any of those views are updated. This behavior allows you to
consolidate all of your layout updates to one update cycle, which is
usually better for performance.
Blockquote
You should perform further operations on the setting of the view by overriding
the layoutSubviews() method
Again, from the docs
Subclasses can override this method as needed to perform more precise
layout of their subviews. You should override this method only if the
autoresizing and constraint-based behaviors of the subviews do not
offer the behavior you want. You can use your implementation to set
the frame rectangles of your subviews directly. You should not call
this method directly. If you want to force a layout update, call the
setNeedsLayout() method instead to do so prior to the next drawing
update. If you want to update the layout of your views immediately,
call the layoutIfNeeded() method.
Hope I have been clear in the explanation.
You can also post your exact situation with code for more clarity and answers.
There is no strait way to do it: UILabel, UIButton haven't such functions because they are inherited from UIControl -> UIView -> UIResponder these classes haven't such functionality.
What you can to do: in your main controller which contains buttons and labels you can call custom method at viewWillAppear which will update content on your custom elements.
PS. Elements you can organize like an array or also check super views and based on protocol / class call your custom method.
With the help of Abhishek Arora, I was able to change the UILabel's textColor this way.
override func setNeedsLayout() {
tintColor = .clear
}
override func tintColorDidChange() {
print("TINT COLOR DID CHANGE")
textColor = .blue
}
}

How can I recall awakeFromNib

I want to recall awakeFromNib when the tableView reload it's cells, to re-execuse the code I am executing there and newly load a pageViewController who is inside the cell, is that possible?
I believe awakeFromNib is called on an object when it is initialized from a nib file. So you can not call it manually.
Read this thread for object loading process from nib file.
See the documentation from apple about awakeFromNIb method
After all objects have been instantiated and initialized, the
nib-loading code reestablishes the outlet and action connections for
all of those objects. It then calls the awakeFromNib method of the
objects.
So, calling it manually looks not possible. Moreover I suggest you to put the lines of code in awakeFromNib into a seperate method and call that method. It will work for you.
As the other respondents point out, you should not call any of the "lifecycle" methods yourself. However, there is no reason on earth why you can't do something like:
override func awakeFromNib() {
super.awakeFromNib()
self.doSetupStuffINeedToRepeat()
}
func doSetupStuffINeedToRepeat() {
//...
}
Then you can simply call doSetupStuffINeedToRepeat() anytime you want. The reason that you shouldn't call awakeFromNib yourself is that, if done properly, it should call super.awakeFromNib, and doing this in the middle of execution may (will) really mess things up...

Why/when do we have to call super.ViewDidLoad?

Everyone tells me "Use super.viewDidLoad() because it's just like that" or "I've been doing it always like that, so keep it", "It's wrong if you don't call super", etc.
override func viewDidLoad() {
super.viewDidLoad()
// other stuff goes here
}
I've only found a few topics about Objective-C cases and they were not so enlightening, but I'm developing in Swift 3, so can any expert give me a good detailed explanation on this?
Is it a case of just good practice or are there any hidden effects?
Usually it's a good idea to call super for all functions you override that don't have a return value.
You don't know the implementation of viewDidLoad. UIViewController could be doing some important setup stuff there and not calling it would not give it the chance to run it's own viewDidLoad code.
Same thing goes when inheriting from a UIViewController subclass.
Even if calling super.viewDidLoad doesn't do anything, always calling it is a good habit to get into. If you get into the habit of not calling it, you might forget to call it when it's needed. For example when subclassing a ViewController that depends on it from a 3rd party framework or from your own code base.
Take this contrived example:
class PrintingViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("view has loaded")
}
}
class UserViewController: PrintingViewController {
override func viewDidLoad() {
super.viewDidLoad()
// do view setup here
}
}
Not calling viewDidLoad here would never give PrintingViewController a chance to run its own viewDidLoad code
If you don't want to do anything in viewDidLoad just don't implement it. The super method will be called anyway.
I have a secret, when I worked at Apple I read the source code for UIKit, partly to answer questions I had like this, viewDidLoad is empty in all the UI*ViewController classes.
Naturally I am not there anymore, they may have changed this.
I think calling super.viewDidLoad() is, first of all, a good practice.
The usual thing in iOS is to do all of your subclass setups after the superclass has completed the setup that it needs to do (initializing properties, laying things out, etc.). If you don't give the superclass a chance to handle all of its setups before you start changing things around, it's possible you'll encounter some strange bugs and behavior.
We can draw a parallel with Class Initialization: "A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property." We're doing this to be sure that all superclass properties have a value and based on that fact we could safely use them through inheritance in our subclass.
Rule of thumb:
When initializing/setting up, run the superclass' implementations first.
When tearing down/cleaning up, run the superclass' implementation last.
Assuming that viewDidLoad() is some sort of initialization we should call super.viewDidLoad() first to correctly set things up in the superclass.
If we check the implementation of viewDidLoad() in UIViewController base class, we can see that it's empty. So maybe the only one reason to calling super.viewDidLoad() from you child class is a good coding style :-) Lets follow it!
That depends on the implementation of viewDidLoad in the class UIViewController (from which all view controllers inherit). If it's empty than calling super.viewDidLoad() won't do much. However if it has some functionality regarding the view controller then you certainly would want to use it.
Since it's not in your hands regarding the implementation of a UIViewController you should always call this method
When inheriting directly from UIViewController, call super when its documentation tells you to.
For example, viewWillAppear(Bool) says, "If you override this method, you must call super at some point in your implementation," whereas viewDidLoad() does not.
If you are not inheriting directly from UIViewController and the class you are inheriting from does not have reliable documentation, or may silently introduce a breaking change requiring that super be called, then always call super.
if your class is been inherited from UIViewController directly then there is no need to invoke super.viewDidLoad. This definately make your code look bit consice but usually iOS community suggest to call it anyway.
if your class is been inherited from custom UIViewController which indeed has some functionality which your class can leverage then invoke super.viewDidLoad.

UIViewController extension not allowing to override view related functions in Swift?

While trying to implement an extension for UIViewController I realise that there is no normal way, or is not allowed to override this functions (even when they are available for UICollectionViewController and UITableViewController):
extension UIViewController{
public override func viewWillAppear(){
super.viewWillAppear()
//do some stuff
}
}
I realise that there is no normal way, or is not allowed to override this functions (even when they are available for UICollectionViewController and UITableViewController):
viewDidLoad
viewWillLoad
viewWillAppear
viewDidAppear
There is some way to do this? I would like to have some implementation there and working for every UIViewController on my app... All in just one place.
Please, note that I don't want to make a new class subclassing
UIViewController, overriding those methods and making my controller to
extend it. This is the obvious and simplest solution, but this do not satisfy what I'm trying to do.
I'm using swift 1.2 in XCode 6.3
What you are trying to do is similar to what done by this code:
class MyClass {
func myFunc() {}
}
extension MyClass {
override func myFunc() {}
}
The 4 methods that you are trying to override are defined in UIViewController, and not to one of its superclasses. And you can't override a method which is defined in the same class.
Update
I can think of 2 different ways to solve the problem - the first is the one you don't want (subclassing UIViewController).
The other one is method swizzling - I never used it so I don't want to provide you inaccurate info. Maybe it's worth reading this article by Nate Cook, which incidentally is showing an example of replacing viewWillAppear.

What would be better strategy for IBActions in protocols in Swift?

I am creating a Swift project and I want to define a specific protocol that enforces other components to implement a animate method:
protocol AnimatableBehavior {
#IBAction func animate()
}
The problem is I want this method to be an IBAction, but I get this error from XCode:
Only instance methods can be declared 'IBAction'
My question is, how would you implement such a thing?
I have considered:
Remove #IBAction, but then I need to remember adding it in every class that implements. Not very elegant and error prone.
Create a base class instead of protocol, but then I am enforcing all components to subclass my base class instead of their own choice ones, so it is not a valid option.
Any other ideas?
EDIT: Response to comments below.
The idea of the IBAction on the protocol is because in the project there will be many different devs implementing small UI components, all of which have the animate method. The components can be added programatically or by Interface Builder and it is very convenient that they are always IBAction because I plan to compose them from IB files to simplify the View Controllers to the maximum extent (and this is clearly a View only task).
Therefore, the solution proposed below of adding a method in the controller that just calls the animate of the component is not good because it is redundant code and makes your Controller more dependent on your View.
The idea of letting the dev to remember adding the IBAction keyword on the method is workable, but as I said it is error prone (and by that I mean that there will be some forgetting about it), and I want to make sure that this is always accessible from IB. It also adds extra cognitive load, because I will need to document this lack of IBAction on the protocol and request the implementor to add it manually.
I know is not the common way of working in iOS and UIKit, but that was why I posted the question, maybe someone has an alternative idea.
It doesn't make any sense to have an #IBAction in a protocol. #IBAction is nothing more than a keyword for Interface Builder to have a hook when you're control+dragging from Interface Builder to your actual source code.
This is just a simple misunderstanding of what #IBAction actually is and does.
A method does not have to be marked as #IBAction in order for it to be the target of a UI element's actions. You programmatically hook up any method to any action using the addTarget set of methods that UI elements have. The method does not have to be marked as an #IBAction to do this.
Regardless of whether or not a protocol defines a method as #IBAction, the class conforming to the protocol can add it (and still be conforming to the protocol.
protocol FooProtocol {
func doSomething()
}
class ViewControllerA: UIViewController, FooProtocol {
#IBAction func doSomething() {
// do something
}
}
class ViewControllerB: UIViewController, FooProtocol {
func doSomething() {
// do something
}
}
Both of these view controller subclasses conform to the protocol, and having #IBAction there is ONLY necessary if you intend to hook up an action from interface builder!
Ultimately, whatever you're trying to do, if you think an #IBAction is necessary in your protocol, I think you're taking the wrong approach to something. It's hard to say what the right approach would be without knowing more details about what you're actually doing, but it never makes sense for #IBAction to belong in a protocol.
To me, it seems like the methods your protocol enforces shouldn't at all be tied to #IBAction methods. Instead, whatever user interaction should trigger the animation, should in turn call the animate method. For example, if we weren't talking about the protocol, my recommendation would be this sort of set up:
class ViewController: UIViewController {
#IBAction func buttonThatStartsAnimation {
self.animate()
}
func animate {
// code that does all the animation
}
}
So, with the protocol, we should take the same seperation of duties between the method that's actually initiating the animation code (which in the case of protocols, this is obviously some other outside class), and the animate method should only ever handle doing the relevant animations.
Importantly, just as a general rule, you shouldn't be directly referring to your #IBAction methods or your #IBOutlet variables directly from outside the class which defines them.
I totally agree with OP, although until Swift 3.1 you can't really declare anything as #IBOutlet, #IBAction, #objc etc in a protocol. As a workaround, I chose to build something based on pod 'ActionKit' and wrote something like:
protocol RequiresAnimation {
var animateButton: UIButton! { get }
func enableAnimateButton()
func actionAnimate()
}
extension RequiresAnimation where Self: UIViewController {
func enableAnimateButton() {
animateButton.addControlEvent(.touchUpInside) {
self.actionAnimate()
}
}
func actionAnimate() {
// animate here
}
}
And make your view controller:
class MyViewController: UIViewController, RequiresAnimation {
#IBOutlet var animateButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
enableAnimateButton()
}
}
I wish there would be any easier approach, but so far you make need to do these 2 things manually: declaring your button as #IBOutlet and call a setup function. The reason why we need to import ActionKit is that we can't addTarget in protocol extension.

Resources