Making a button persistent across all view controllers
Yes, adding it to the UIWindow would be extremely hacky and finicky.
Storyboards
If you're using Storyboards and iOS 5.0 onwards, you should be able to use container views and do something like this:
Here's another picture showing the, rather simplistic, structure of the first View Controller:
The view controller on the left has a container, and then a view which holds the button on top of it. The container indicates that the navigation controller (directly to the right) should appear within itself, that relationship is shown by the =([])=>
arrow (formally known as an embed segue). Finally the navigation controller defines its root view controller to the one on the right.
In summary, the first view controller pancakes-in the container view with the button on top, so everything that happens inside has to have the button on top.
Using childViewControllers
aka. The "I hate Storyboards and puppies" mode
Using a similar structure to the Storyboard version, you could create the base view controller with its button, and then, add the view that will become then new "root" of the application, underneath.
To make it clear, let's call the one view controller that holds the button the FakeRootViewController
, and the view controller that will be, for all practical purposes, the root of the application: RootViewController
. All subsequent view controllers won't even know that there's the FakeRootViewController
above everyone else.
FakeRootViewController.m
// The "real" root#import "RootViewController.h"// Call once after the view has been set up (either through nib or coded).- (void)setupRootViewController{ // Instantiate what will become the new root RootViewController *root = [[RootViewController alloc] <#initWith...#>]; // Create the Navigation Controller UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:root]; // Add its view beneath all ours (including the button we made) [self addChildViewController:nav]; [self.view insertSubview:nav.view atIndex:0]; [nav didMoveToParentViewController:self];}
AppDelegate.m
#import "FakeRootViewController.h"- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; FakeRootViewController *fakeRoot = [[FakeRootViewController alloc] <#initWith...#>]; self.window.rootViewController = fakeRoot; [self.window makeKeyAndVisible]; return YES;}
That way, you can have all the benefits of inserting the button on the window, without all the guilt and "Should I really be a programmer?" that it causes.
Potentially you could have 1 main "root" view controller, and all you other view controllers could be child view controllers, with their views as child views. Then they would have their content, and the button would be in the "root" view controller. But this seems just as sketchy and hacky as putting it in the window, and probably less convenient.
I use this button:
@interface UIPopUpButton : UIImageView <UIPopoverControllerDelegate, UIActionSheetDelegate>{ UIPopoverController* popoverController; Class popoverClass;}- (id) initWithPoint: (CGPoint) point;- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event;+ (id) buttonAtPoint: (CGPoint) point;+ (id) buttonAtOriginalPoint;+ (void) unhighlight;+ (void) bringButtonToFront;@property (nonatomic, retain) UIPopoverController* popoverController;@property (nonatomic, assign) Class popoverClass;@end#import "UIPopUpButton.h"@implementation UIPopUpButtonstatic UIPopUpButton* button = nil;static CGPoint originalPoint;@synthesize popoverClass;@synthesize popoverController;+ (id) buttonAtPoint: (CGPoint) point{ if (button == nil) { button = [[UIPopUpButton alloc] initWithPoint: point]; originalPoint = point; button.popoverClass = [UIPopoverController class]; } else { button.frame = CGRectMake(point.x, point.y, button.frame.size.width, button.frame.size.height); } return button;}+ (id) buttonAtOriginalPoint{ return [self buttonAtPoint: originalPoint];}+ (void) unhighlight{ button.highlighted = NO;}+ (void) bringButtonToFront{ [[UIApplication sharedApplication].keyWindow addSubview: [self buttonAtOriginalPoint]];}- (id) initWithPoint: (CGPoint) point{ UIImage* image1 = [UIImage imageNamed: @"topbutton.png"]; UIImage* image2 = [UIImage imageNamed: @"topbutton.png"]; if ((self = [super initWithImage: image1 highlightedImage: image2])) { self.userInteractionEnabled = YES; self.frame = CGRectMake(point.x, point.y, self.frame.size.width, self.frame.size.height); self.multipleTouchEnabled = NO; } return self;}- (BOOL) isAppCurrStatus{ return ([DevToolsClientController sharedInstance].statusOfRootViewController == FrontEndApplication);}- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event{ UITouch* touch = [touches anyObject]; if(touch.view == self) { if (self.popoverController == nil) { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle: @"Please choice operation:" delegate: self cancelButtonTitle: nil destructiveButtonTitle: nil otherButtonTitles: nil]; [actionSheet addButtonWithTitle: @"Cancel"]; actionSheet.cancelButtonIndex = 0; [actionSheet addButtonWithTitle: @"Button 1"]; actionSheet.actionSheetStyle = UIActionSheetStyleDefault; [actionSheet setTag: 0]; [actionSheet setDelegate: self]; [actionSheet showInView: [self superview]]; [actionSheet release]; [actions release]; } else { PopoverMenuController* contentViewController = [[PopoverMenuController alloc] init]; self.popoverController = [[UIPopoverController alloc] initWithContentViewController: contentViewController]; popoverController.delegate = self; [popoverController presentPopoverFromRect: CGRectMake(10.0f, 10.0f, 5.0f, 5.0f) inView: self permittedArrowDirections: UIPopoverArrowDirectionAny animated: YES]; contentViewController.popoverController = self.popoverController; [contentViewController reloadData]; } } else { [self.popoverController dismissPopoverAnimated:YES]; self.popoverController = nil; } } [super touchesBegan: touches withEvent: event];}#pragma mark UIActionSheetDelegate implementation-(void) actionSheet: (UIActionSheet*) actionSheet clickedButtonAtIndex: (NSInteger) buttonIndex{ NSNumber* indexAction = [[NSNumber alloc] initWithInt: buttonIndex - 1];}- (void) runAction: (NSNumber*) indexAction{ [DevToolsPopoverMenuController runAction: [indexAction integerValue]];}#pragma mark -#pragma mark UIPopoverControllerDelegate implementation- (void) popoverControllerDidDismissPopover: (UIPopoverController*) thePopoverController{ if (self.popoverController != nil) { self.popoverController = nil; }}- (BOOL) popoverControllerShouldDismissPopover: (UIPopoverController*) thePopoverController{ //The popover is automatically dismissed if you click outside it, unless you return NO here return YES;}@end
call:
[UIPopUpButton bringButtonToFront];
My button is always on top.