NSNotification, addObserver and removeObserver - removeObserver both in ViewDidUnload and dealloc? - objective-c

I want to add a view controller as observer of a notification.
When the selector method is called, I'll alert the user. I would that this alert appears only when this view controller is the top view controller.
I thought to add it as observer in the viewDidLoad method. But where is the better remove the view controller as observer?
Could it be a problem if I remove it both in viewDidUnload and dealloc?

No. According to the documentation:
Removes all the entries specifying a given observer from the receiver’s dispatch table.
If there are no such entries, there is nothing to remove, so it's not a problem.
However, depending on how you're using this, adding it as an observer in viewDidLoad doesn't mean that it will only be used when that view controller is "the top view controller" (for instance, if you have several tabs and move from one to the other, unless you get memory warnings the other (non-visible) tabs' views won't be unloaded when you move away from them).
Depending on your goal, it might be better to add the observer on viewDidAppear (and if so, remove on viewWillDisappear).

Related

Should we call ads in viewDidLoad or viewDidAppear?

We have a tab-heavy app, which has 5 tabs to use back and forth. We have iAds and admobs(as backup for countries without iAd), and we 'call' the ads in viewDidLoad. Would it make a difference to call them in viewDidAppear instead? And then remove them in viewDidDisappear or shomething not to screw up the frames etc? Would this give more impressions etc?
viewDidLoad:
viewDidLoad
Called after the controller’s view is loaded into memory.
- (void)viewDidLoad
Discussion
This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the loadView method. You usually override this method to perform additional initialization on views that were loaded from nib files.
viewDidAppear:
viewDidAppear:
Notifies the view controller that its view was added to a view hierarchy.
- (void)viewDidAppear:(BOOL)animated
Parameters
animated
If YES, the view was added to the window using an animation.
Discussion
You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must call super at some point in your implementation.
Answering
So viewDidLoad is called slightly earlier than viewDidAppear: , the only difference is that when viewDidAppear: the view have been already drawn, instead in viewDidLoad the view has still to be drawn.
So answering to your questions:
Would it make a difference to call them in viewDidAppear instead?
If calling the ads is a slow operation, then you would see first the view appearing in it's color, and the ads after a few interval of time.However this has to be too slow to make a real difference.
And then remove them in viewDidDisappear or shomething not to screw up the frames etc?
It doesn't "screw up frames", that for sure.
you just need call it in viewDidLoad
Putting your ad code in viewDidAppear: (and removing it in viewDidDisappear:) will certainly give you more impressions, but unless you're a whitelisted pub, you're probably getting paid on a cost per click basis anyway (AdMob Help Center article).
In this case, instead of having the overhead of creating and destroying GADBannerView objects on tab changes, you might as well create a singleton GADBannerView that you use throughout your TabbedController (look at an example here).

Adding NSNotification observers to view controllers on a storyboard

I am putting a test app together to demonstrate how users can be notified when something happens in the app (using NSNotifications). The user should be notified by an unobtrusive banner at the top of the screen regardless of what view controller is displayed at the time. I have the code already to draw the banner, but i am having problems setting up the NSNotification observers.
I have a storyboard with two viewcontrollers. How do I reference their init methods so i can add observers in for the NSNotification posts?
View controllers in a storyboard will be init'ed using initWithCoder:. You would usually have a separate set up method that is called from this, and from initWithNibName:bundle: just to cover you for all use cases.
Or, start observing in viewDidLoad and stop in viewDidUnload.

What is called when removing a view from its superview?

Imagine I have a view controller called blueView and another called greenView. I then add greenView.view as a subview of blueView.view. Now suppose that after some user interaction, I'd like to remove greenView.view from blueView.view using:
[self.view removeFromSuperview]
What is actually happening here? Is blueView.view ever redrawn? I thought the viewDidLoad method might be called, however after putting an NSLog messge in viewDidLoad, it was never called after removing the subview. Any clarification as to what is actually happening when you remove a subview from its superview would be much appreciated.
First, a view controller is meant to manage an entire view hierarchy at once; you shouldn't have two view controllers (other than container controllers like UINavigationController) active at the same time. See this SO question and my answer to get a better understanding on this important point. So, the particular situation you describe shouldn't come up. (Aside: people often confuse views and view controllers, so it's not helpful to give your view controllers names ending in "-view", like "blueView." Call it "blueViewController" to help avoid confusion.)
Second, as #InsertWittyName points out, -viewDidLoad is a UIViewController method, not a UIView method. Taking that a step further, neither view controllers nor -viewDidLoad has any role in adding or removing subviews from a view. -viewDidLoad is called when the view controller's view is first created. It's basically just a way of deferring the view-related part of view controller initialization until after the view hierarchy has been created, so there's no reason that it'd be called again just because a subview was removed from the hierarchy.
Finally, exactly how a view removes itself from its superview is really an implementation detail -- it might call a private UIView method on the superview, or it might modify the superview's list of subviews directly, or something else. I don't see anything in the documentation that explicitly says that the superview will redraw itself after a subview has been removed, but in my experience the superview does indeed redraw itself. You can check this by putting a breakpoint on the -drawRect method of the superview.

UITextView: Must I always resignFirstResponder?

Must I always resignFirstResponder for a UITextView? Or, will this happen automatically when its view controller disappears?
I'm asking because I'm having an issue similar to iPhone Objective-C: Keyboard won't hide with resignFirstResponder, sometimes, where the keyboard stays up even when the nav controller pushes and pops other view controllers. The keyboard works, and when I hit done, it unfocuses the UITextView (i.e., the cursor disappears), but the keyboard stays up.
I never found out why this is happening, but maybe it's due to not doing resignFirstResponder before pushing another view controller, but I thought it was optional?
At a total guess, the UITextView has a reference to the view controller (as its delegate) but does not retain it. When you go to the next screen, the controller is dealloced and then the UITextView (which has perhaps been retained by something else) tries to call back to the dealloced controller and crashes. When you call resignFirstResponder, you reverse the order this happens, and therefore no crash.
The way round this to add a textView.delegate = nil call in your view controller's dealloc method - obviously put it before you release the text view.
The contract between a UITextView and it's delegate says that the delegate will send -resignFirstResponder when the text view is done editing. This informs the framework that the view is done editing, fires the events relating to that (willEndEditing and didEndEditing), and allows other parts of the responder hierarchy to react accordingly. Failing to do so might work, but it's not following the contract (that's all a protocol is) it agreed to.
I don't think you have to because the Xcode Sample UICatalog UITextField doesn't call resignFirstResponder before the TextViewController is popped.
The reason the keyboard got stuck for me is that I was having the same view controller present two view controllers modally at the same time, one after the other. UIKit didn't like that.
Calling resignFirstResponder makes sure that the text property contains the actual text shown in the control.
Depending on the state this is not always necessary, but if your controls have resigned first responder, you know that you're working with valid data.

Get UINavController from subview of UIView?

I added a custom subview to a UIViewController's content view. From that subview, how can I get a reference to the superview's controller?
Thanks
The correct answer is "You're Doing It Wrong™" ;-)
You shouldn't need to reference to a view controller from a view, and you certainly should never retain a view controller in one of your views -- or you'll end up with a retain loop and leak memory.
Cocoa provides multiple patterns to solve this problem:
Use a delegate: Define a protocol called DemoViewDelegate and add a delegate property to DemoView. Then have your view controller implement this protocol. Important: delegates should never be retained! Any delegate property you create should be set to assign. See Apple's Delegation docs or just google "delegation pattern".
Use the responder chain: Call UIApplication's sendAction:to:from:forEvent: and leave to: set to nil to have your action message automatically routed up the responder chain to your view controller. See Apple's Responder docs and the more detailed Action Messages docs.
Use a notification: Less common in this particular scenario, but you could also have your view controller listen for a notification, which the view sends.