Notice a view is loaded from outside of a viewController - iphone

Any help would be appreciated.
I am looking for a way to programatically notified that a view is loaded from outside of that viewController.
Lets say my main view has 5 buttons, after the view is loaded and the buttons appeared I want to be notified in another file (outside of that viewContrller) that it is loaded. How/Where can I check this and be notified?
Do I need to do some Aspect Oriented Programming?

Use NSNotificationCenter. You can communicate between classes.

NSNotificationCenter or a delegate method is the most appropriate way to accomplish this.

Listen for a custom NSNotification inside your other object. Have your view controller post that notification during whichever part of its life cycle makes most sense (viewDidLoad, viewDidAppear ...).
If you can't post a notification, then observing a keyPath might be the way to go. For example, you can put something like this in your control object and then implement observeValueForKeyPath::
[viewController addObserver:self
forKeyPath:#"view"
options:NSKeyValueObservingOptionNew
context:NULL];

While you can do this with notifications as others have suggested or KVO, this strongly suggests a design problem. You should never be accessing a view controller's internal views directly. So the deeper question is: why do you want to know?
The most likely cause in my experience is that you're letting some other object set the titles or modify enabled. This breaks MVC and leads to the kind of problems you're probably trying to fix. The correct way to handle this is to put the data into a model object that is shared between the various view controllers. The current view controller can then observe changes on the model and update its UI elements appropriately.

Related

MVC design issue with notification from view to controller

mine is not really a programming problem but a design problem regarding the Model-View-Controller design pattern in IOS programming. I am new to IOS but I really want to do the things in the right way. So I have this view that does some animations but I want to "notify" the Controller when the animations end. I know that the View cannot have a reference to the Controller. Reading the page: https://developer.apple.com/library/ios/#referencelibrary/GettingStarted/RoadMapiOS/chapters/StreamlineYourAppswithDesignPatterns/StreamlineYourApps/StreamlineYourApps.html
In the first figure the view - controller communication is only user action (IBActions from the view) and update from controller. Possible solution that I have thought:
Notification, even it does not exist in the figure it could be OK
Controller polls the status of a property of the view until the animations finishes (might block the main thread if I'm not wrong)
Key Value Observing, seems to be like a Notification.
Do you have any idea or best practice to exchange? Thanks in advance
While you could use NSNotifications, they're better used when you want to notify multiple objects, or you don't know what object you want to notify. It could also be used if there's no easy way to get a reference to the view from the controller, but that's not usually the case.
So, the one way you left out, delegation, is probably the best approach. The view would define a delegate protocol, and call a delegate method when its animations end. The controller, which would set itself as the delegate of the view, would implement that delegate method to respond appropriately to the end of those animations.
Since you didn't provide any code I'll assume that the ViewController is the object that caused the animation. If so, then the ending of the animation should be handled inside a block. Example:
[UIView animateWithDuration:0.5
animations:^(void) { your animation code here }
completion:^(BOOL finished) { your completion code here }];

The ultimate guide for writing parent view controllers

Short version
If I'm writing my own custom split view controller, what do I need to do to make the child view controllers work as expected?
For instance: to send viewWillAppear: and so on.
Long version
Background
A while back I answered the following question:
Switch between UIViewControllers using UISegmentedControl
With the following answer:
The right way to do it is to have the controller handling the UISegmentedControl add the views of the controllers as subviews.
[self.view addSubview:controller.view];
It's your responsibility to send viewWillAppear: and so on.
However, tc. pointed out that this is not as trivial as it sounds:
No. View controllers are not meant to be used like that - controller will miss out on a lot of the UIViewController magic that's taken for granted (namely -view{Will,Did}{Appear,Disappear}: and -shouldRotateToViewOritentation:).
By "magic", I'm referring to everything UIKit does behind the scenes. You also forgot -parentViewController (which is important for things like modal view controllers). Additionally, somewhere in the depths of UIKit, it automatically calls -viewSomethingSomething: for you, so you might get -viewDidDisappear: twice! (I can't remember the exact details, but there's another user reporting that all you need to do is call -viewWillAppear: and the other three methods happen automatically.) The key issue is that Apple doesn't document the "magic" or how it changes between OS updates.
Since then I've been thinking that one should compile a guide for what needs to be present in the parent view controller in order for the child view controllers to work as expected. Apple's documentation doesn't cover this and Google didn't help much either, so now I'm hoping to find this knowledge within the stackoverflow community.
I've written several controllers like this and they all seem to work, but I can't help but wonder if I'm missing out on important view controller magic.
I think the best solution is "don't do that". Instead of hoping that you can duplicate all of UIViewController's behavior without being rejected for using private API calls why not create non-UIViewController controller objects to manage your subviews? A "controller" is not necessarily a UIViewController.
At a minimum you would need to override or mix in replacement getters for parentViewController, splitViewController, navigationController, tabBarController, and interfaceOrientation (and probably also modalViewController). For each property you would need to make sure that any private setter called by UIKit still works as expected and any changes to those values made by modifying UIViewController ivars directly are also correctly reflected in your implementations.
You're also going to need to figure out how UIKit determines which UIViewController is currently active and should receive view controller lifecycle methods because you need to make sure that these are sent to your container view controller and not only to one of it's children.
You will also have to hope that you haven't just constructed a situation which isn't supported by any of Apple's view controller classes. For example will any of them break if they have a parentViewController but their navigationController, tabBarController, and splitViewController are all nil?
Finally you'll need to keep up with any changes to these private implementation details with every iOS release.

How to set the delegate to another ViewController?

I've recently started developing for the iPhone and so far I'm doing pretty good but there's this basic pattern I really don't seem to get.
Say, I have a TabBar with two views and a custom delegate protocol, thus my structure is the following:
AppDelegate.h/.m
myDelegateProtocol.h
FirstViewController.h/.m
SecondViewController.h/.m
MainView.xib
FirstView.xib
SecondView.xib
Now I want to achieve the following: I placed a button in the FirstView.xib and I'd like the IBAction which it invokes (inside FirstViewController ofc.) to send a message to the SecondViewController ([self.delegate tellSecondViewContrToSayHi]) and invoke another method which simply prints a log into the console saying "hi I'm here."
So far I know what I need to do in theory:
Specify the protocol.
Implement the protocol in the SecondViewController.
Create an id< myDelegateProtocol > delegate inside my FirstViewController,...AND last but not least:
Set the self.delegate = secondViewControllerObject.
Now, nr.4 is where the problem's at. How on earth do I link the delegate to the other viewController? I mean I'm not the one instantiating the views as the tabBar kinda does that for me,... any advise? Or am I just way too tired to notice a really stupid thing I did somewhere?
Theoretically the same question also applies to the target:action: thing,... I mean, how do I define the target?
Thanks a lot,
wasabi
You have the right idea, assuming that you want relatively tight coupling between these controllers via that delegate protocol.
Since neither controller knows about the other until that delegate property is set you need to have some object which has a reference to both of them wire up that relationship. In your case that's probably the application delegate which can create both controllers, set one as the delegate of the other, and pass both along to your tab bar controller.
What you might actually want is to have the app delegate give both controllers a reference to some shared model object. Your FirstViewController can update that model when you tap a button and your SecondViewController can observe changes to the model to update it's display (or just update its view when it appears based on the current model state). That way your controllers don't need to know anything about each other.

Is this a bad idea?

I've been working on this problem for two days now. I'm working on an iPhone app that, at the moment, has "dual layer" view (see picture.) The semi-transparent orange panel covering the left third of the screen was created by simply resizing the sub-view (in IB) to take up less than half the screen so that, when that view loads, the original view is still exposed on the right. This would allow the left view to be a "menu view" allowing a user to select what he or she would like to appear in the main view window (which is actually a UIWebView...see screen shot.)
----- Click Here For Screen Shot-----
If I'm going to keep this setup (assuming it's not a structural sin), the left-view clearly needs a way to communicate with the main view. Can I invoke methods in the main-wiew ".m" file (WebViewController.m) like viewDidLoad and others from the "ETG" button on the orange subview? Or is this just a really bad idea? And if this isn't a bad idea or a sin against iPhone structure, how would you implement it? I'll thank you in advance for any helpful thoughts or suggestions you might have. Thanks!
If you're following the model-view-controller pattern, which you generally should, then your view should send messages to the controller or modify the model, not another view. Although it really depends on what you're doing. In your case you are using the panel as a control, so you should implement in a fashion that makes it independent of other views.
Usually the only time you have views directly manipulate other views is in layout, and that is normally in a top-down fashion.
Again, these are general rules and there are always exceptions.
What you are describing doesn't seem insane, but they way you go about talking to the main view might need a little work.
It seems like what you want to do is have the overlay view have a delegate that it can send messages to. Does that seem like it would work for you?
Umm, is the web view the main view?
Either way, I'd do this:
One view controller that contains two main areas. One is your UIWebView, and another is your layover. If you do this in Interface Builder, put the Layover on top of the UIWebView.
All you have to do is animate it in and out based on certain input. A bad idea is to say "hide menu" and make the UIWebView take up all the space, so you can't get it back.
Then use use one view controller for both.
The recommended method of communicating between view controllers, if that's subviewed, is to create properties in the view controller that you can pass. Eg: the web view needs to tell the main view what site it's on. So put an NSString in your main controller as a property, then pass it the string on viewWillDisappear or whatever that name is.
(Or, use viewWillAppear on the top level and have it grab that property from the 2nd view).
Essentially it's just a branching tree, and you have to pass data up one node to reach the others.
You "can" use the application's delegate itself/(the application) and from anywhere, call [UIApplication sharedApplication].property (after creating a property), and use it as a global, but that's not considered reusable code. Since you're used to basic, it might work for you.
Finally, C++ globals do work, and there are many examples on the web for using globals in an iPhone program with externs. (even less recommended).
Now, it sounds like you need to read the Views Programming Guide, even though it has severe grammar issues in areas (they may have corrected the homonyms in the 3rd paragraph of the intro by now, but other areas are totally confusing because of that), to get an understanding of how views respond to input, and what happens when input is ignored and bubbles up the tree. (that's basically what it does, lol... but layers and views have intricacies and it's good to understand them and how they function together).
No, it's not a bad idea. But without understanding layers, view animation, and design maybe it is, until you do.
NazCode
If I understand what you're trying to do, one approach is to use notifications. This way none of the objects involved need to have references to one another. Before I learned this approach, I had several awkward cases where I seemed to be working much too hard just to get two objects to talk to one another.
In your case, your orange layer can post notifications and the controller for the UIWebView can listen for them.
So when you tap the button in the orange layer, do something like this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"etgTapped" object:self];
And in controller that looks after the webview add something like this to your viewDidLoad method:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(etgTapped:) name: #"etgTappe" object:nil];
And then create an etgTapped: (NSNotifaction*) notification method in that class.
Finally, in viewDidUnload de-register with the notification centre:
[[NSNotificationCenter defaultCenter] removeObserver:self];

Objective C Callbacks and Notifications

I'm new to Objective-C and not a full time programmer. I'm beginning to understand the Model-View-Controller design pattern for differentiating the UI from the model. So the user takes an action and the view controller sends a message to the delegate (model). But I'm not sure what the best way to send actions from the delegate back to the view controller.
For example, the user pushes a button, the VC messages the Delegate. That part I understand. Then the delegate takes action, and following that the delegate wants to update the VC (e.g., update a label).
So what I missed (or have forgotten) is how this gets done, while maintaining separation between the UI and the model. I suppose I can use the notification center. Or I think I can just have the view controller pass a callback to the delegate. Or maybe there's another choice I don't know of. Can someone give me a recommendation, please?
I think you're slightly misunderstanding the MVC paradigm. Models should never be delegates of views, since models should have no dependencies or knowledge of any view classes. Typically, a view sends a message to its delegate or target (if you're using target/action), which is usually a controller (often a subclass of UIViewController on iOS). The controller then accesses data from the model and can update any views that need updating. I'd recommend reading the MVC fundamentals guide for a more complete explanation.
Basically you're right, you could do all the notification-related things yourself (i.e. with NotificationCenter) but since we're talking about UI-Stuff here I would greatly recommend you to use IBAction-Methods and IBOutlet-Properties in your code which you can easily connect to UI-Elements respectively their Callbacks in Interface Builder.
A very basic introduction to this topic can be found here:
iPhone SDK Interface Builder basic training
i hope that it is not too basic tough, and that I could lead you on the right track.
First of all delegate is NOT a Model.
Model is something passive that only holds the data (DB, plist, array, dictionary etc.).
While delegate is some set of functions that exist in order to react to some events.
Delegate is more likely to be a view controller in your case.
The view controller should react to user's action.
If the button tap should display some data from your model in some label then view controller should do all the work (receive user's action, take the necessary data from the model and display it on the view...).