I'm trying to work out the "best" way to use a UISegmentedControl
for an iPhone application. I've read a few posts here on stackoverflow and seen a few people's ideas, but I can't quite sort out the best way to do this. The posts I'm referring to are:
Changing Views from UISegmentedControl and How do I use a UISegmentedControl to switch views?
It would seem that the options are:
UIView
and animate it left/right or up/down depending on the selected segmentUITabBarController
to swap out the subviews - seems sillycellForRowAtIndex
and populate the table from different data sources or sections based on the segment option selected (not the case for my app)So which approach is best for subview/non-table approaches? Which is the easiest to implement? Could you share some sample code to the approach?
Thanks!
I'd go with the second option you mention, creating the subviews in IB and swapping them in and out of a main view. This would be a good opportunity to use UIViewController
, unsubclassed: in your initial setup, create a controller using -initWithNibName:bundle:
(where the first parameter is the name of the NIB containing the individual subview, and the second parameter is nil
) and add its view
as a subview of your main view as necessary. This will help keep your memory footprint low: the default behavior of a UIViewController
when receiving a memory warning is to release its view if it has no superview. As long as you remove hidden views from the view hierarchy, you can keep the controllers in memory and not worry about releasing anything.
(edited in response to comment:)
You don't need to subclass UIViewController
, but you do need separate XIBs for each view. You also don't need to add anything to the containing view in IB.
Instance variables, in the interface of whatever class is handling all this:
UIViewController *controllerOne; UIViewController *controllerTwo; UIViewController *currentController; IBOutlet UIView *theContainerView;
In your setup (-applicationDidFinishLaunching:
or whatever)
controllerOne = [[UIViewController alloc] initWithNibName:@"MyFirstView" bundle:nil]; controllerTwo = [[UIViewController alloc] initWithNibName:@"MySecondView" bundle:nil];
To switch to a controller:
- (void)switchToController:(UIViewController *)newCtl { if(newCtl == currentController) return; if([currentController isViewLoaded]) [currentController.view removeFromSuperview]; if(newCtl != nil) [theContainerView addSubview:newCtl.view]; currentController = newCtl; }
Then just call that with, e.g.,
[self switchToController:controllerOne];
I've come across this requirement as well in an iPad application.
The solution I came to was to create specialized view controllers for each style of view to handle business logic relating to those views (ie. relating to each segment), and programatically add/remove them as subviews to a 'managing' controller in response to selected segment index changes.
To do this, one has to create an additional UIViewController subclass that manages UISegmentedControl changes, and adds/removes the subviews.
The code below does all this, also taking care of a few caveats/extras:
Interface:
@interface SegmentManagingViewController : UIViewController <UINavigationControllerDelegate> { UISegmentedControl * segmentedControl; UIViewController * activeViewController; NSArray * segmentedViewControllers; } @property (nonatomic, retain) IBOutlet UISegmentedControl * segmentedControl; @property (nonatomic, retain) UIViewController * activeViewController; @property (nonatomic, retain) NSArray * segmentedViewControllers; @end
Implementation:
@interface SegmentManagingViewController () - (void)didChangeSegmentControl:(UISegmentedControl *)control; @end @implementation SegmentManagingViewController @synthesize segmentedControl, activeViewController, segmentedViewControllers; - (void)viewDidLoad { [super viewDidLoad]; UIViewController * controller1 = [[MyViewController1 alloc] initWithParentViewController:self]; UIViewController * controller2 = [[MyViewController2 alloc] initWithParentViewController:self]; UIViewController * controller3 = [[MyViewController3 alloc] initWithParentViewController:self]; self.segmentedViewControllers = [NSArray arrayWithObjects:controller1, controller2, controller3, nil]; [controller1 release]; [controller2 release]; [controller3 release]; self.navigationItem.titleView = self.segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Seg 1", @"Seg 2", @"Seg 3", nil]]; self.segmentedControl.selectedSegmentIndex = 0; self.segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar; [self.segmentedControl addTarget:self action:@selector(didChangeSegmentControl:) forControlEvents:UIControlEventValueChanged]; [self didChangeSegmentControl:self.segmentedControl]; // kick everything off } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.activeViewController viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self.activeViewController viewDidAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.activeViewController viewWillDisappear:animated]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self.activeViewController viewDidDisappear:animated]; } #pragma mark - #pragma mark UINavigationControllerDelegate control // Required to ensure we call viewDidAppear/viewWillAppear on ourselves (and the active view controller) // inside of a navigation stack, since viewDidAppear/willAppear insn't invoked automatically. Without this // selected table views don't know when to de-highlight the selected row. - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { [viewController viewDidAppear:animated]; } - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { [viewController viewWillAppear:animated]; } #pragma mark - #pragma mark Segment control - (void)didChangeSegmentControl:(UISegmentedControl *)control { if (self.activeViewController) { [self.activeViewController viewWillDisappear:NO]; [self.activeViewController.view removeFromSuperview]; [self.activeViewController viewDidDisappear:NO]; } self.activeViewController = [self.segmentedViewControllers objectAtIndex:control.selectedSegmentIndex]; [self.activeViewController viewWillAppear:NO]; [self.view addSubview:self.activeViewController.view]; [self.activeViewController viewDidAppear:NO]; NSString * segmentTitle = [control titleForSegmentAtIndex:control.selectedSegmentIndex]; self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:segmentTitle style:UIBarButtonItemStylePlain target:nil action:nil]; } #pragma mark - #pragma mark Memory management - (void)dealloc { self.segmentedControl = nil; self.segmentedViewControllers = nil; self.activeViewController = nil; [super dealloc]; } @end
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With