My objective is to show dealer locations in two different ways: a map and a tableview.
I have a container view controller (DealersViewController) which contains two child view controllers: DealersMapViewController and DealersListViewController. The user can switch between the VCs with a UISegmentedControl placed on the navigation bar. Tapping on a map annotation or a tableview cell will push a DealersDetailViewController.
The switching is already implemented (using code from Changing view controller when Segmented Control changes) and seems to work fine, as does the pushing of the detail.
What I would like to improve is the flow of data between container and children. The dealer locations are downloaded (JSON) from the internet in the parent and on completion an NSArray *locations property is set on both the map VC and the list VC. This array will contain dictionary objects created automatically by AFNetworking, each with location data (each location dictionary will have a title, subtitle, latitude, longitude to conform to MKAnnotation protocol, but also other things like image and description etc).
My question is: How can I be sure that the container VC and both child VCs 'agree' on how location data is structured? Theoretically, if someone wanted to develop another child view controller to add to my container that shows dealer locations in a collection view for example, how can he formally know how to expect data.
Apple says: "If the container needs the child to declare methods or properties, it should define a protocol to enforce this:". I could force the children to declare the locations property but the array could contain anything…
Maybe the parent could be a datasource for the children? I haven't tried this approach yet.
I am probably overcomplicating things but my objective is also to learn how to properly create reusable components and also practice using stuff like custom protocols / delegates and design patterns in general.
Thanks.
If I understand correctly, your issue is structuring data so that all of your controllers have the same understanding of it.
Probably the best way to go around it is to create a custom location class, and have the JSON deserialize into an instance of that class and then just pass it around as you see fit. There's quite a few ways how to go around deserializing, but JSON Model is a nice example of how to handle it automatically.
Tl;dr: convert your JSON dictionaries to custom classes and then pass them to your child view controllers, via properties or delegates, whichever you find more convenient.
Related
I've two exactly similar views which I show in a two segmented control. Refer to image below. The differences between these two views is the parameter which I send to backend to fetch the values and the title. Even the returned values are same.
I've referred to some tutorial which cycles from one view to another when segment is selected .
I've ended up with two files with exactly same code. How to optimise this implementation so that I can implement with one piece of code only.
The two contained VCs have exactly same code to fetch values from backend and display. I've only one function which I've used in both VCs to fetch but there are other code sections such as Tableview delegations and other codes which are common to both.
In storyboard both are duplicate as well.
Is there anyway I can make it more efficient?
This could be a case for making your two view controllers be subclasses of a common superclass.
Or it could be even simpler: make them two instances of the same view controller class, which knows what to do because you pass a parameter at creation time telling it what to do.
For example, my Albumen app uses four view controllers, which differ primarily just in what query they perform on the user's music library. So I've elected to make them four instances of one view controller class, with an enum property saying which query it is, and any other varying functionality determined through switch statements on that enum.
I think you need to create only 1 VC ( In IB and code ) , put all the logic inside it and either
1- Add once instance / container of it to the MainVC and manage the process of selecting a segment to reload the content ( Recommended )
2- Add 2 instances of it to the MainVC and manage hide/show when the segment is selected
I'm having a brain cramp for some reason on this. Some already posted questions/answers are outdated or objective-C. I'm not sure if delegate is the way to go?
I have a hierarchy of UIViews.
In phenotypeSunplotView:UIView some data is set, a string. I want to pass a string to another class of UIView which actually does the plotting, SunplotView:UIView
For example, I want to pass the string "Gorgonzola" to the class that plots the circle, SunplotView:UIView and plot the string next to the circle.
As a general rule, you don't. Views are not data storage objects. They are the "View" in the Model/View/Controller design pattern.
Instead, you set up the view controller to install the data in both places. Give your view controller outlets to all views that it needs to change.
If a view is an input field (like a UITextField for example) then you add an action or delegate method in your view controller that gets called when the user enters data. Then you collect that data and copy it to any other views that are appropriate. The view controller mediates as needed to implement your app's logic.
I have an app that was extremely simple until today. It had a tab bar view controller with 3 tabs. The middle tab was a camera, and the other 2 were table views. The tab bar view controller was the central hub for all the data in the app. So from there, I would set a table's data array as:
(PLEListViewController*)[self.viewControllers objectAtIndex:0] setList:newList];
Obviously, PLEListViewController is my UITableView subclass.
So now, I want to wrap the table views in a UINavigationController, which is fairly simple. But now, that line of code turns into:
[(PLEListViewController*)((UINavigationController*)[self.viewControllers objectAtIndex:0]).topViewController setList:newList];
There are 15 lines in the code that do this, which is not pleasant.
So my question: what is a more elegant way of doing this that I'm missing?
It's good that you're asking this and seeing the issue now. Your problem is can be found in your question. The answer to "the correct way to communicate between several different view controllers in Objective C" is "don't." Specifically, your mistake is here:
The tab bar view controller was the central hub for all the data in the app.
A view controller should never hold any of the data in the app. Your data should live in your model classes. All the view controllers should talk to the model classes. They should very seldom talk to each other. That's the heart of MVC.
So, you move your "list" (whatever that is, doesn't matter) into some model object that all the view controllers know about. That model object can be a singleton, or often better, it can be passed to the view controllers when they are created. When things change, you change the model. And in viewWillAppear: you update your view controller to match the current state of the model.
Never assume that a view controller exists when it is not currently on screen. If your design requires that a non-active view controller exist, then your design needs fixing.
You need to work with your architecture. Make the appropriate datasource and delegation protocols to ensure your classes can communicate anonymously. What you currently have is very inflexible and it will get worse as your app grows/changes.
You want to make things more loosely-coupled, instead of coding explicit traversal of links between your objects in your code.
Assuming you have one data model that is displayed in various places in your application, I think there are 2 approaches that could help...
One is to use your view controller hierarchy.. For example, use [ self enclosingTabBarController ] to find your closest parent tab bar controller and get it's data model property. Substitute -enclosingTabBarController with what works better for your application.
The other approach would be a "data model as a singleton" approach. For this you can either
move the data to your application delegate and access it via ((MyApplicationDelegateClass*)[ UIApplication sharedApplication ].delegate).dataModel
or
have a singleton data model object for your app, and access it via [ MyDataModelClass sharedModel ]
In any case you are moving to a looser coupling, which requires less explicit traversal of links between objects in your app. Less is more!
I'm new to obj-c/iOS and I'm having trouble understanding conceptually at least viewControllers. I've read a lot of the Apple Doc's, I've even used viewControllers to some extent in xCode, but I still don't quite get what they are, or what are the best ways to use them.
I've been an AS3 dev for many years so my mind works in the context of MovieClips/Sprites and the Display list to get graphics on the screen.
Ok so from my understanding...
A viewController is a kind of class that handles graphics in some
fashion and then allows you to do something with them?? What is it in it's most basic sense?
You seem to add viewControllers to a Window class, which I guess is a bit like
adding a display Object to the Display list?
What is it that a viewController does for you in it's most basic sense?
Are there certain things you definitely can't do with them or shouldn't do
with them?
Do viewControllers need to be connected in some way to the rest of the iOS framework to function (apart from being added to a window).
How exactly do they use data? (I've read up on MVC, I understand that conceptually this is a slightly different question) as I understand it you don't hardcode data into a viewController, so how does a viewController access any static data?
Let's say I just wanted to throw an image up on the screen, exactly what part would the viewController play in that process? is it just something which handles only one small aspect of that process or is it the whole show and handles everything?
Does one viewController handle multiple images? is it like it's term, a "controller" for all the images presented on screen, or does it handle one image at a time?
What is a viewControllers connection to the image(s) it handles? it contains references to them?
I'm using the Sparrow framework which is helping but I would still like to be able to get my head around what viewControllers are so I know how to use them properly.
Ha, I apologise for the above I know it must look like I'm completely confused :) thanks for any advice.
Hope this helps you:
A viewController is a kind of class that handles graphics in some fashion and then allows you to do something with them??
It's the glue between a View (Xib File) and the Data (Could be
CoreData or whatever you're using in the backend). All the UI Elements
you are using in the View you normally define as properties in the
controller to get access to them.
What is it in it's most basic sense?
You seem to add viewControllers to a Window class, which I guess is a bit like adding a display Object to the Display list?
I don't really know AS3 so I cannot compare Display lists with ViewControllers. But basically ViewControllers are there to handle
different types of transitions between the views and accessing
(setting/reading) the data which is displayed in the view.
What is it that a viewController does for you in it's most basic sense?
Like I've written above. Most basic sense they interpret what the user
does on the view and depending on the action of the user changes the
model.
Are there certain things you definitely can't do with them or shouldn't do with them?
It is always hard to keep the border between model and controller.
They are pretty close to each other. So what I normally try is to
delocate all logic stuff (like calculations, database access and so
on) this does more belong into the model part. But of couse you're
using these external classes in the controller.
Do viewControllers need to be connected in some way to the rest of the iOS framework to function (apart from being added to a window).
Well like you already have written the ViewController needs to be
connected to a view. Otherwise it would not make much sense. There are
different subtypes of UIViewController such as UINavigationController
where you probably need to overwrite some other methods to provide the
whole functionality wanted by these special subtypes.
How exactly do they use data? (I've read up on MVC, I understand that conceptually this is a slightly different question) as I understand it you don't hardcode data into a viewController, so how does a viewController access any static data?
There could be different approaches to store the data. Simplest way
would be to have the data directly stored in the UIViewController.
This could be a custom class which is the container of the data. All
changes are directly written into this class and displayed by the
UIViewController. But in most of the cases it makes sense to use
CoreData (Which is responsible for reading/writing the data into a
sqlite database). You could look at CoreData as your model and the
UIViewController gets the data from there and passes the data which
the UIViewController has received from the View back to it.
Let's say I just wanted to throw an image up on the screen, exactly what part would the viewController play in that process? is it just something which handles only one small aspect of that process or is it the whole show and handles everything?
The UIViewController would store an internal Property (UIImageView *)
which is in the Interface Builder connected with the UIImageView you
have created in the Xib file. So over these property you can change
through your Controller the image.
Does one viewController handle multiple images? is it like it's term, a "controller" for all the images presented on screen, or does it handle one image at a time?
Yes, this isn't a big problem. You can have as many images you want.
You just need to have the properties defined in the UIViewController
and linked to the View.
What is a viewControllers connection to the image(s) it handles? it contains references to them?
Yeah, its like a reference to the UIElement. You can then change
whatever property of the UIImageView you want directly from the
UIViewController
Some useful links:
Apple Official ViewController Guide
Apple Official ViewController Basics
You should have a look at Storyboards (U can use them since IOS 5.0)
I recommend you to check:
https://stackoverflow.com/questions/1939/how-to-articles-for-iphone-development-and-objective-c
Here are the answers to your questions:
No, it's doesn't handle graphics. It's the controller of the MVC design pattern. It handles the lifecycle of it's contents (for instance the views) and the data linked with.
A UIViewController is set as a root of an UIWindow. For instance, a UINavigationController is a subclass of UIViewController that stacks UIViewController in order to deal with the navigation.
Response in (1)
Try to be more specific with this question please.
As already commented, it's useful if you use the already built-in components like UINavigationController or UITabBarController.
For instance, you can have the data in instance variables and the display them in the contained UIView.
The UIView attached to your UIViewController will contain an UIImageView. Your UIViewController would have a connection with it in order to whatever changes you need, for instance, changing the image when the user press a button.
It can contain multiple UIViewsand therefore multiple UIImageViews (it's a subclass of UIView)
As commented, they would be contained on an UIImageView and would be linked programmatically or with an IBOutlet.
In a nutshell, a view controller is the controller in the MVC pattern. Please check this link before reading further so you're up to date with this pattern:
http://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html
OK, basically a controller manages a collection of views. It also fetches data from your model and sets the state of the views. It's important to note that the views know nothing of your model (your data), and the model knows nothing about your views. A controller also receives events from the views and decides how to change your model accordingly. It is essentially managing the synchronisation between your views and model.
There are technologies that help automate this such as KVO and key value binding. A google search will help you there.
One more thing. No other part of your application should access your views except for the controller. So generally in an application controllers tend to communicate with each other, for example via transitions or the delegate patterns between controllers under a navigation controller. So your application backbone tends to be controllers talking to each other.
Explanation
My app basically uses a mapview with an overlay of polygons that represent buildings, coupled with an annotation. So for this it imports a custom class called Annotation that handles the popup details when the annotation is tapped, meaning it store the building's name and address. At this time the callout (the blue disclosure button) loads an empty DetailViewController object (as there's not really any data to pass through).
I soon added a new feature in the form of a searchable table that loads custom objects of the Building class (with similar properties to Annotation, plus images and more details) that then loads in the aforementioned DetailViewController class with the building's details.
So to summarise, the MapVC contains multiple annotations, which when the relevant disclosure button is tapped open the DetailVC. SearchVC is accessed by a button on the MapVC and has a table of Building objects, which loads a DetailVC with the relevant data, like so:
Next Step
So now I want to implement functionality into the blue disclosure button on the callout, so when the user taps it it'll load the building details. At the moment all it has is the annotation's details. I could add the extra properties to the annotation to make it complete but I think it's much better to just work with one custom class called Building, which has a MKAnnotation nature. Then this Building class is loaded for the annotations and searchVC's table.
Question
So finally, what's the best way to go around this? I want all the data to be stored independently of any of the VCs in the diagram. I followed a tutorial from Apple (the BirdSighting one) which uses a separate Datacontroller class, which I'd then load into other classes. Is this the best approach?
You're talking about MVC, model-view-controller. This is a very smart way to handle things, and is a good practice to get into. The model stores the data, in this case, building names, etc. The view displays data. The controller is what connects the view to the model, updates the view, gets notifications from users and in turn updates the model.
I use a singleton pattern for model data. Only one instance of a singleton is ever present in an application. That way, the data is not bound to any one particular view controller. Data only gets updated in once place, the model. It's a lot easier to trouble shoot issues with a singular point of convergence for application data objects.
I have a macro that I define in my PCH file.
#import "DataController.h"
#define DATA() [DataController sharedInstance]
In my code I can easily get to my model class by calling
DataController *data = DATA();
All of my views and viewcontrollers access objects stored in DataController, so there are never two view controllers handling separate pieces of information independently. All references point to the same place.
Some people use the AppDelegate object to store data, but it can quickly turn into a 1000 line beast. I prefer to keep the AppDelegate clean :-)