I'm building a location app that show the user some places around him.
I have a NearbyPlacesViewController
with a segment control that has two buttons "list" and "map"
If user press on "list" - I show him a table view with the list of places around him
If user press on "map" - The view flips around and I show the user a mapView with the places on it as pins.
In "list" I also use a UISearchBar and a UISearchDisplayController to search the tableView
In "map" I also have some other subviews beside the mapView
Currently I keep all of the views (UITableView, MKMapView, UISearchBar
and more...)
and the delegate methods (UITableViewDelegate, UITableViewDataSource, MKMapViewDelegate, UISearchDisplayDelegate
, and more..)
in the NearbyPlacesViewController
.
When user press on "map" button I hide all of the views relevant to the "list" view (tableView, search bar...), and I un-hide all of the views relevant to the "map" view (mapView, some other subviews...), I then use UIView transitionWithView
to do flip animation between them.
Everything works but It seems a bit messy, and the result is a big NearbyPlacesViewController with a lot of code and delegate methods.
Is it better to do it with separate viewControllers?
And if so, how do I do it? do I create ListViewController and MapViewController and put them in a NearbyViewController?
How do I share the model between them?
Think ModelViewController. If they are truly only views that you are switching between, use of one UIViewController is appropriate, that's what I usually do with segmented controls because it usually comes down to switching around the view for the current controller. The controller should definitely handle the IBAction for the segmented control.
Delegates and Datasources were meant to be extracted into a separate class when it made sense, and in your case it does. I would consider separate classes for the various delegates you are using, this will clean it up significantly while also sticking close to the design principles Apple intended. You could keep the UITableView delegate and datasource together in their own class, but other than that, creating a separate class for each different delegate will clean it up significantly.
When dealing with a segmentedControl this is how I've done it in the past. You might be able to create a separate controller class to handle the underlying model for everything and clean out some of that code, but that really just depends on how far you want to take it.
In my opinion, your best option would be to have three View Controllers and create Modal Segues between them. (I assume you're using storyboards.) You would have your parent View Controller with two children (List & Map).
(1) In your storyboard (that has ParentViewController, ListViewController & MapViewController) create a Modal Segue from each button to the child VCs. Give them identifiers (showList & showMap would work well) and choose Transition:Flip Horizontal.
Set up the protocol and delegates: (I'll show you how to do one, then just repeat it.)
(2a) In ListViewController.h add above @interface:
(2b) Add the delegate as a property:
(3a) In ListViewController.m synthesize:
(3b) And delegate in the IBAction button method to flip back:
(4) In ParentViewController.h add at the very top
#import "ListViewController.h"
and on the end of @interface<ListViewControllerDelegate>
(5a) In ParentViewController.m add the method to conform to the protocol:
(5b) Then set it as the delegate in prepareForSegue:
Just so you get a sense of what view controller containment might look like (iOS 5), this is one way to do it. It consists of four classes, the container view controller (whose view will have the segmented control for switching between two child view controllers), two random child view controllers, and a model class where we'll store the data (which can be accessed by the two child view controllers).
First you create a container view controller with your segmented control (I also added a UIView which basically defines the frame where child view controllers' views will be placed, just to make it easier to figure out where to put that view):
And then you implement it:
My model just has two data elements, an array of objects, and a string, but obviously you can do whatever you want. I'll just show the header (as the implementation details are trivial and uninteresting):
And the child view controllers are similarly trivial:
And the implementation might look something like (this is a trivial example, but shows how you can retrieve information from the shared model):
Hopefully this gives you a sense of how you could use separate view controllers for your two views, how to switch between them, make your model accessible to both. This is a fairly simple example, but it's functional. There are some optimizations I might suggest, but hopefully this is enough to get you going in the right direction.
A couple of options leap out at me:
If you use a
UISegmentedControl
, you could have twoUIView
objects for your two screens (you can create them in interface builder or build them programmatically) and then you could just hide/show oraddSubview
/removeFromSuperview
as you jump between them. Since all of the data will be managed by your single view controller, this is pretty easy. If the two subviews get really complicated, though, this single view controller might get pretty hairy. But should work fine.If you wanted separate view controllers, you would probably pursue iOS 5's view controller containment (see Implementing a Container View Controller in the UIViewController reference, or see WWDC 2011 session 102), though this is a little more complicated. You could store your data as properties of the container view controller which are either passed to or reference by the child controllers.
If you're not wed to the segmented control UI, you could just use the
UITabBarController
which is ideally suited for this scenario (it's effectively a permutation of the previous option, but the container view controller, the tab bar controller in this case, is already written for you). One view controller for each of the two views, and store the data either in theUITabBarController
custom class, a singleton, some persistent storage (like user defaults, core data, sqlite), etc.Seperate view controllers is probably a neater way to go. I prefer them. Also means you can make the initial view controller the parent to the others and lay out the various views you've got and add your child view controllers as subvuews to the views of the parent as and when their needed. Also makes it easier to add some custom animations or relay out at a later date.