In my scenario I have 2 view controllers, one tied to the main view and one that is connected to the first one as a subview.
Now let's say that my App Delegate class wants to pass a string to the subview controller. What is the best practice to achieve this? If I wanted to pass it to the 1st controller i could just say something like
[self.firstController getStringInCustomFunction:[NSString stringWithFormat:#"200%d", 9]];
Also keep in mind that this call might have to be asynchronous.
Coming from ActionScript, normally I would just add and event listener and move my variables though events. What's the equivalent in objective-c?
Coming from ActionScript, normally I would just add and event listener and move my variables though events. What's the equivalent in objective-c?
Take a look at NSNotificationCenter.
Specifically, postNotificationName:object:userInfo, wherein you create an NSNotification that includes an NSDictionary of objects you pass inside userInfo.
On the other end, you have another object that is registered to "hear" an NSNotification of a specific name. That other object calls whatever method is specified in the registration. You might unpackage the userInfo dictionary in this method, to retrieve the object of interest.
Related
I'm a starting iOS developer and I would like to get the idea of NSMutableArray.
Specifically, I want to pass NSString value from one VC to NSMutableArray, belonging to another view controller. I also want that NSMutableArray to populate UITableView on its view. Any ideas how to do this?
For example, I have this action that the user triggers from within 1st view
- (IBAction)addToFav:(id)sender {
}
And I want to pass a value of NSString myBookString to the NSMutableArray array in the 2nd view.
The task is fairly simple, but I can't quite understand why you can't pass the values to NSMutableArray outside this - (void)insertObject:(id)anObject atIndex:(NSUInteger)index method mentioned in Apple's reference docs.
Thanks in advance!
EDIT: My views are different VC belonging to UITabBar and to UINavigationController at the same time
You need a communication between two controllers. There are several solutions. Pick one of the following. I recommend delegate.
From http://www.hollance.com/
When you have two objects A and B, say two view controllers, that you want to make talk to each other, you can choose from the following options:
NSNotificationCenter.
This is anonymous one-to-many communication. Object A posts a notification to the NSNotificationCenter, which then distributes it to any other objects listening for that notification, including Object B. A and B do not have to know anything about each other, so this is a very loose coupling. Maybe a little too loose…
KVO (Key-Value Observing).
One object observes the properties of another. This is a very tight
coupling, because Object B is now peeking directly into Object A. The
advantage of KVO is that Object A doesn’t have to be aware of this at
all, and therefore does not need to send out any notifications — the
KVO mechanism takes care of this behind the scenes.
Direct pointers.
Object A has a pointer to Object B and directly sends it messages
when something of interest happens. This is the tightest coupling
possible because A and B cannot function without each other. In the
case of view controllers you generally want to avoid this.
Delegates
Object B is a delegate of Object A. In this scenario, Object A does
not know anything about Object B. It just knows that some object
performs the role of its delegate and it will happily send messages
to that delegate, but it doesn’t know — or care — that this is Object
B. The delegate pattern is often the preferred way to communicate
between view controllers, but it takes some work to set up.
Blocks.
Essentially the same approach as delegates, except that Object B now
gives Object A one or more blocks (closures) to be executed when
certain events take place. There is no formal delegate protocol and
the only thing that Object A sees of Object B is the blocks it is
given.
NSMutableArray is just that, a standard mutable array implementation. It has all of the functionality you would expect from one in any other language/framework. docs.
What is the problem with the way adding elements to an array works? You can also just do [array addObject:object] if you don't mind adding it to the tail.
To answer the rest of your question, there are many ways to transfer data from one controller to another. How is your navigation set up? Are you using a UITabBarController, or a UINavigationController? How are the views related to each other? If they are nested, you can pass data directly from one to another. If not, you could use something like your app delegate to hold the data and pass it around.
Usually I just put together an app in any random way, as long as it works, but this means I don't pay any attention to any design patterns. My application currently makes extensive use of global variables (I carry an instance of my AppDelegate in every view controller to access properties I declared in my AppDelegate.h). Although it does what I want it to do, I've read this is not a good design practice.
So I want to start making my code "legit". However, I can't imagine my app right now without global variables. They are so important to the well-being of the app, but this must mean I'm doing something wrong, right? I just can't imagine how else I'd do some things. Take for example this:
You have two view controllers here, a SideViewController and a MainViewController. Using global variables, such as say if the entire application had one shared instance of SideViewController and MainViewController (appDelegate.sideViewController and appDelegate.mainViewController), I can easily communicate between the two view controllers, so that if I press "News Feed" in my SideViewController, I can tell my MainViewController to reload it's view.
I can't imagine, however, how this would be done if these were not global variables? If an event occurs in my SideViewController, how would I notify my MainViewController, in a way that is in accordance with design standards?
I can't imagine, however, how this would be done if these were not
global variables? If an event occurs in my SideViewController, how
would I notify my MainViewController, in a way that is in accordance
with design standards?
The same way you do it now, except that the SideViewController gets its reference to the MainViewController from a different place.
How are these two view controllers created? It's likely that it happens in one of two ways:
One of the objects creates the other. In this case, maybe the MainViewController creates the SideViewController.
Some other object, such as the app delegate or another view controller, creates them both.
In the first case, the MainViewController has a reference to the SideViewController as soon as it creates it. It can store that reference in one of its own instance variables, so that it can always send messages to the SideViewController that it created. Similarly, the MainViewController can give the SideViewController a reference to itself (that is, to the MainViewController), and the SideViewController can store that and use it in the future to talk to its MainViewController.
The second case is similar -- if the app delegate (or some other object) creates both MainViewController and SideViewController, that object knows about both objects and can configure each with a reference to the other.
In both cases, the objects in question are able to communicate with each other just as easily as they ever did and there's no need for a global variable.
What I've explained above is perhaps the simplest way to accomplish what you asked for -- communication between two objects. There are a number of patterns that can be used to refine the relationship between those objects to make your code even better:
delegation: Give SideViewController a delegate property, and define some protocol that establishes what SideViewController expects of its delegate. Implement that protocol in MainViewController. Make your MainViewController instance the SideViewController's delegate. SideViewController doesn't need to know exactly what type its delegate is -- it only cares that it's delegate implements the required protocol. This makes it easy to use SideViewController with something other than MainViewController if that opportunity arises, or to use it in a different project.
notifications: SideViewController may not even need a delegate -- it can simply broadcast notifications about certain events to any object that happens to be listening. This is particularly effective if more than one object might need to know about something that happens in SideViewController, or if the objects that care about SideViewController's actions might change.
MVC: Instead of telling MainViewController that something has changed, SideViewController just changes the data in the model. Whenever the MainViewController's view appears (or any other view controller's view, for that matter), the controller reads the data from the model and redisplays itself.
If you're interested, you might want to pick up a copy of Erik Buck's Cocoa Design Patterns, which explains these patterns and many others in great detail. Don't feel like you have to learn it all at once, or that it's all too much trouble. Learn a little bit at a time and see how it improves (or doesn't) your projects.
I have a fairly complex application at this point, and feel as though the way I am handling data is not up to scratch.
So I have started taking some steps towards making my app more MVC friendly.
The first of which, I have gone from having inline parser methods in all of my ViewControllers to creating a dedicated parser class.
Its not hard to call I just set up the class then call the initializer method I have made which I pass the data to for my request.. everything in my object class works sweet I get a whole bunch of return data from the server that I am parsing.. but then this is where I get abit lost.
How do I get that data back to the ViewController that called it? I have worked a little with protocols and delegates.. but I dont know how that would fit into this.
Here is an graphical example of what I am trying to achieve.
So as above View controller calls the initalization method of the object class which connects to the DB downloads the data, and then parses that data. The issue I am having is how do I then get that data to ViewController2..
what is the most appropriate and future proof way of doing this?
A custom -init method does wonders. For instance, I needed to instantiate and push a picker preloaded with the array from the previous screen, so I defined -(id)initWithArray:(NSArray*)array in the second view, then just called secondView = [[SecondView alloc]initWithArray:_population]; then pushed it. Remember to get ownership of any object you pass before it is deallocated and destroyed!
If you want to broadcast data from your object to all view controllers the good way to do this will be by notifications (NSNotificationCenter). You could also write a methods with completion blocks inside your parser object.
Try to send a local notification using NSNotificationCenter. Register your ViewController2 as a listener, then upon receiving the notification, call your data model (singleton!?) for the data.
I have had about 6 hours trying to work out custom application delegates. even following tutorials i end up with errors!
I need a simple way of allowing one object to talk to another.
I have a root view. then another view which then pushes onto the stack with a list of options. then another view showing the relevant options based on the previous selection.
So basically 3 views, and i need view 3 to pass data back to view 1 when i popBackToRootViewController.... This is becoming a huge headache, for something that in theory should be so simple. I have previously just thrown data into nsuserdefaults but using protocols in the way apple suggest to do it.
Please can someone help me understand :)
There a different ways to solve that problem. First of all, you could pass the first View over and over to the View you are making changes on and then call a method of view 1. I won't recommend that.
Another, much easier way is to use Notifications. Just register your first View in the notification center [[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(somethingChanged) name:#"aStringWichIsUniqeForCallingSomethingChanged" object:nil];. You have to provide and implement a callback method (somethingChanged in my case). In your subwiew where the things happen, you have to post a notification by doing [[NSNotificationCenter defaultCenter] postNotificationName:#"aStringWichIsUniqeForCallingSomethingChanged" object:nil];. And don't forget to remove the view from the notificationcenter if they are not needed anymore!
[[NSNotificationCenter defaultCenter] removeObserver:self];
A third possibility is to use a singelton (like the app delegate) which contains all views that should communicate. Simple make all views as ivars & properties for them in this singleton and implement in each of them methods which should be called if something changes. Then call [[singelton sharedInstance] view1] somethingChanged].
If i say View, i mean viewController, but i'm to lazy to write that. ;)
Hope, that helps!
Protocols and delegation formalize a concept which works even if you don't follow the formal rules: It's about declaring a way for objects to talk to each other about the same thing, especially in those cases where you either don't have or don't want control about one of the objects, or in cases where things get complex and you want everything nice and tidy so it's easier to reuse the code.
In those cases however where a rather small, well known set of information is to be passed back and forth between two (or more) well known objects which are unlikely to be reused, informality is quite ok.
This means you could:
provide the sending object with a property of the type of the receiving object
implement a method in the receiving object which will take the information to be sent as an argument
at some point, for example in your case when the root controller pushes the child which becomes the sending object, the receiving object will set itself as the value of the sending object
if the sending object has information to send, it uses the reference to the receiving object to send it a message calling the earlier mentioned method, passing the information as an argument
As I mentioned before, this makes sense for glue code. You shouldn't be doing it this way though, if:
you're working in a team and you're working on one object and a teammate is working on the other
it's obvious you may be able to use the same functionality in a future project. In this case, think about having an intermediate subclass between your original superclass and your app-specific subclass, one that encapsulates the core functionality and offers a formal protocol to interface with it
the exchange of information involves objects of different classes within the same app
the exchange of information itself is rather complex and one (or two) glue methods wouldn't cover it
I have setup and successfully logged in via xAuth using an extended class of MGTwitterEngine, my question is if I want to pass this to another view controller, how can I change the delegate class, as it is some sort of weak reference
#interface MGTwitterEngine : NSObject <MGTwitterParserDelegate> {
__weak NSObject <MGTwitterEngineDelegate> *_delegate;
Am I best wrap this up into a singleton class and pass around that way, seems overkill to login in each time, or have I missed a painstakingly obvious way of sharing this object around
At the moment I have added a setDelegate method to the MGTwitterEngine but feel as though I am fighting the framework unnecessarily
If you're sharing the engine across multiple objects then you would want to have some other object/singleton wrap the engine and act as its sole delegate. If you've done database programming then think of it like a database connection -- you probably wouldn't have each view controller create its own database connection. Instead you'd create some sort of data manager object that is shared by the views and possibly abstracts away some of the DB internals.
If different view controllers handle different tasks -- like login, looking up users, querying messages, etc. then the delegate methods in your wrapper should be able to pass the responses along to the appropriate view controller.
If you have different view controllers calling the same methods (and if so, why?), you could still route responses back to the corresponding view controllers. As the MGTwitterEngine docs say, "Each Twitter API method returns an NSString which is a unique identifier for that connection." You would just need to pass an object (your view controller) or a block as an extra parameter to each of your wrapped methods. You can cache the twitter id string and this object/block in a mutable dictionary when your wrapper sends the response, then look up the connection id in the cache when it's time to handle the response.
actually, you can.
The delegate, is nothing but a variable in the MGTwitterEngine. Just add a instance of it in the next view controller adding the proper header and inplementation calls.
after instatiating the new view controller set:
nextViewController._mgTwitterEngine = self.mgTwitterEngine;
nextViewController.mgTwitterEngine.delegate=nextViewController;
then call the nextViewController.
Do not forget to set the delegate back to the original view controller when you return to it (either on viewDidAppear or viewWillAppear)
Hope that helps...
Best Of luck!
Use NSNotifications in the delegate.
Make the view controller where you wish the delegate to be add an observer. Have the delegate method for MGTwitterEngine post the notification.