My goal is to draw an invisible button above the status bar on the top of my iPhone app (dimension 320*20 pixels).
No matter what I try, something is buggy:
For example, I tried to create a new view. When I want to place the view on the top of my app, it always disappears behind the status bar instead of being in front of it!
I found another great idea on Stackoverflow:
Add UIView Above All Other Views, Including StatusBar
Even if a second UIWindow isn't recommended, I tried to implement it. It worked as I wanted until the moment that I noticed a problem: the keyboard doesn't appear anymore when needed (for example when clicking in a textbox)!
How can I possibly fix this? Or is there a better approach to my problem? This is my code for creating the second window:
// Create window
statusWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0,0,320,20)];
statusWindow.windowLevel = UIWindowLevelStatusBar;
[statusWindow makeKeyAndVisible];
// Create statusBarButton
statusBarButton = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect buttonFrame2 = statusBarButton.frame;
buttonFrame2.size = CGSizeMake(320,20);
statusBarButton.frame = buttonFrame2;
[statusBarButton addTarget:self action:#selector(goTop) forControlEvents:UIControlEventTouchUpInside];
// Place button into the new window
[statusWindow addSubview:statusBarButton];
in AppDelegate file
self.window.windowLevel = UIWindowLevelStatusBar;
or in any other class
[AppDelegate appDelegate].window.windowLevel = UIWindowLevelStatusBar;
and if you want to make status bar to be on the top again u can set
self.window.windowLevel = UIWindowLevelNormal;
In the documentation for -[UIWindow makeKeyAndVisible]:
This is a convenience method to make the receiver the main window and displays it in front of other windows. You can also hide and reveal a window using the inherited hidden property of UIView.
The "key window" is the one that gets certain input events; text fields in a non-key window might not work. There are two things you can do:
Call [window makeKeyAndVisible] on the old key window afterwards
Set statusWindow.hidden = NO instead (but I don't see why it would be hidden by default). I don't think you can "display it in front of other windows" like -makeKeyAndVisible does; windows don't have a superview you can call -bringSubviewToFront: on IIRC.
If other users want to implement this, here my solution:
// Create window
statusWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0,0,320,20)];
statusWindow.windowLevel = UIWindowLevelStatusBar;
// Dont make the statusWindow keyWindow or the keyboard won't work!
// [statusWindow makeKeyAndVisible];
// Create statusBarButton
statusBarButton = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect buttonFrame2 = statusBarButton.frame;
buttonFrame2.size = CGSizeMake(320,20);
statusBarButton.frame = buttonFrame2;
[statusBarButton addTarget:self action:#selector(goTop) forControlEvents:UIControlEventTouchUpInside];
// Place button into the new window
[statusWindow addSubview:statusBarButton];
// Instead, add this:
[window makeKeyAndVisible]; // has to be main window of app
statusWindow.hidden = NO; // without this the statusWindow won't appear
Try:
[self.view bringSubviewToFront:statusWindow];
I dont think its possible to bring a view in front of the status bar though.
Related
I was able to create an app using Storyboard - it has a navigation controller containing a table view controller with a search display controller and worked as expected.
I then decided to customize it by adding a UIButtonBarSystemItemSearch (magnifying glass) to the navigator bar so that when touched it would display the search bar over the navigator bar, just like setDisplaysSearchBarInNavigationBar does. To do this I removed the UISearchDisplayController from the storyboard and added it programmatically as shown in the code below. The btnSearchClicked function is called when the magnifying glass is touched, so the button is hooked up right.
My project is very similar to the Apple example project "AdvancedTableSearch" but without the scope bar.
- (IBAction) btnSearchClicked:(id)sender
{
//Set up search bar
UISearchBar *mySearchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
[mySearchBar setDelegate:self];
[mySearchBar setShowsCancelButton:YES animated:NO];
// Set up search display controller
UISearchDisplayController *mySearchController = [[UISearchDisplayController alloc] initWithSearchBar:mySearchBar contentsController:self];
mySearchController.delegate = self;
mySearchController.searchResultsDataSource = self;
mySearchController.searchResultsDelegate = self;
//mySearchController.displaysSearchBarInNavigationBar = YES;
mySearchController.navigationItem.titleView.opaque = NO;
[self.navigationController.navigationBar addSubview:self.searchDisplayController.searchBar];
}
I am having two problems with this approach. The most important one is that even though I set the delegate properties, when I type into the search bar none of my delegate functions are called (specifically shouldReloadTableForSearchString).
The second problem is that the cancel button doesn't display despite calling the setShowsCancelButton method.
I've been learning iOS for the last year and reading everything I can find on SO, but can't figure this one out. This is my last resort and I will fall back to the old storyboard approach with standard search bar if I must. The reason I like this other approach is that I have a very long list of contacts (~5000) and don't want to have to scroll to the top to get the search bar.
Looks like you're not adding the right searchDisplayController? you added self.searchDisplayController but inited a mySearchController. Try putting the last line as:
[self.navigationController.navigationBar addSubview:mySearchController.searchBar];
Is it possible to add a UIView on the staus bar of size (320 x 20)? I don't want to hide the status bar, I only want to add it on top of the status bar.
You can easily accomplish this by creating your own window above the existing status bar.
Just create a simple subclass of UIWindow with the following override of initWithFrame:
#interface ACStatusBarOverlayWindow : UIWindow {
}
#end
#implementation ACStatusBarOverlayWindow
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Place the window on the correct level and position
self.windowLevel = UIWindowLevelStatusBar+1.0f;
self.frame = [[UIApplication sharedApplication] statusBarFrame];
// Create an image view with an image to make it look like a status bar.
UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:self.frame];
backgroundImageView.image = [UIImage imageNamed:#"statusBarBackground.png"];
[self addSubview:backgroundImageView];
[backgroundImageView release];
// TODO: Insert subviews (labels, imageViews, etc...)
}
return self;
}
#end
You can now, for example in a view controller in your application, create an instance of your new class and make it visible.
overlayWindow = [[ACStatusBarOverlayWindow alloc] initWithFrame:CGRectZero];
overlayWindow.hidden = NO;
Be aware of messing with the window key status by using - (void)makeKeyAndVisible or similar. If you make your main window (the UIWindow in your Application Delegate) loose key status, you will encounter problems with scrolling scrollviews to top when tapping the status bar etc.
I wrote a static library mimicing Reeders status bar overlay, you can find it here: https://github.com/myell0w/MTStatusBarOverlay
It currently supports iPhone and iPad, default and opaque black status bar styles, rotation, 3 different anymation modes, history-tracking and lots of more goodies!
Feel free to use it or send me a Pull Request to enhance it!
All answers looks like working, but in iOS6.0 I have next problems:
1/ Rotations looks bad
2/ Window (status bar is kind of Window) needed rootViewController
I'm using answer from myell0w, but rotate works not good. I've just remove one extra window and using UIWindow from AppDelegate to implement status bar.
May be this solution is ok only for one UIViewController-app...
Ive implemented by the next way:
1/ In ApplicationDelegate:
self.window.windowLevel = UIWindowLevelStatusBar + 1;
self.window.backgroundColor = [UIColor clearColor];
self.window.rootViewController = _journalController;
2/ Create custom UIView and implement all that you need inside:
For an example touchable statusbar:
#interface LoadingStatusBar : UIControl
And easily create and add to your controller view:
_loadingBar = [[LoadingStatusBar alloc] initWithFrame:topFrame];
[self addSubview:_loadingBar];
3/ Some magic when add your controller view (in initWithFrame:)
CGRect mainFrame = self.bounds;
mainFrame.origin.y = 20;
self.bounds = mainFrame;
Your controller view will has 2 views - content view and status bar view. You can show status bar, or hide it when you want.
Frame of content view will be:
_contentView.frame = CGRectMake(0, 20, self.bounds.size.width, self.bounds.size.height);
4/ And one last magic here :)
To detect touches in non touchable area I've used:
-(id)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (point.y < 20) return _loadingBar;
return [super hitTest:point withEvent:event];
}
For now it works fine on iPad/iPhone and all iOS's from 4 to 6.
Just to dismiss the "You cannot do this comments"...
I don't know how but I know it is doable. The Feed reader app called Reeder does that.
As you can see from the screenshot, Reeder puts a small dot on the top right of the screen. When you tap it. The bar will fill the whole statusbar until you tap it again to make it small.
First of all, a big thank you to #Martin Alléus for providing the code for this implementation.
I'm just posting for a problem that I faced and the solution I used, as I believe others might experience the same issue.
If the App is started while an call is in place, the status bar height will be 40 pixels and this means that the custom status bar will be initialized with that height.
But if the call is ended while you are still in the app, the status bar height will remain still 40 pixels and it will look weird.
So the solution is simple: I've used the Notification center to subscribe to the status bar frame change delegate of the app and adjust the frame:
- (void)application:(UIApplication *)application didChangeStatusBarFrame:(CGRect)oldStatusBarFrame {
//an in call toggle was done
//fire notification
[[NSNotificationCenter defaultCenter] postNotificationName:kStatusBarChangedNotification object:[NSValue valueWithCGRect:oldStatusBarFrame]];
}
And in the ACStatusBarOverlayWindow we subscribe to the notification:
-(id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Place the window on the correct level & position
self.windowLevel = UIWindowLevelStatusBar + 1.0f;
self.frame = [UIApplication sharedApplication].statusBarFrame;
self.backgroundColor = [UIColor blackColor];
//add notification observer for in call status bar toggling
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statusBarChanged:) name:kStatusBarChangedNotification object:nil];
}
return self;
}
and our code to adjust the frame:
- (void)statusBarChanged:(NSNotification*)notification {
//adjust frame...
self.frame = [UIApplication sharedApplication].statusBarFrame;
//you should adjust also the other controls you added here
}
The kStatusBarChangedNotification is just a constant I've used for easy referrence, you can simply replace it with a string, or declare the constant globally.
I have created scroll view and set the buttons in the scroll view. The buttons are scrolling horizontally. I created table view as subview for view controller. On clicking the buttons, the datas are displaying in the table view from RSS feeds using XML Parsing. SO changing the datas in the table view which depends the button clicking. When i changed to select the next button, the parsing will be started and displayed the datas. so it takes some times. In the mean time i want to display some view or disable the view(that means,on that parsing time the view is disable or user cant do anything like freeze with activity indicator). On changing the each buttons, the action will be happened. I referred some tutorials, but i cant get any idea? Some people told me to use synchronous method to solved the problem. But i don't have any idea about it. Please guide me and help me out.Give me some sample apps and Links.
see my below image,
Thanks in Advance!
Regards,
Pugal
Don't use synchronous network calls to disable user input. Whoever suggested that gave you very bad advice.
If you just want to disable input for your current view and its subviews, you can do self.view.userInteractionEnabled = NO; in your view controller.
If you want to disable input for the entire window, you can do self.view.window.userInteractionEnabled = NO;
You won't need to disable user interaction at all if you overlay a full-screen view on top of your user interface. Based on your mock-up image, I think this is what you're trying to do. To do this, you can do something like this:
self.overlayView = [[[UIView alloc] initWithFrame:self.view.window.bounds] autorelease];
self.overlayView.backgroundColor = [UIColor blackColor];
self.overlayView.alpha = 0.5f;
[self.view.window addSubview:self.overlayView];
self.activityIndicator = [[[UIActivityIndicator alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
self.activityIndicator.center = self.view.window.center;
[self.view.window addSubview:self.activityIndicator];
[self.activityIndicator startAnimating];
self.activityLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
self.activityLabel.text = #"Loading...";
[self.activityLabel sizeToFit];
self.activityLabel.center = CGPointMake(self.activityIndicator.center.x, self.activityIndicator.center.y - self.activityIndicator.frame.size.height);
[self.view.window addSubview:self.activityLabel];
The iPhone SDK 3.0 has this handy new class "UISearchDisplayController" which makes it easy to create a search bar, handling all the user input and displaying the search results.
I am using it for my new app, but there is one thing i would like to change:
As a default, the search bar should be put at the top of the UITableView that displays the search results. So when scrolling down the result list, the search bar disappears.
What I would like to achieve is having the search bar always on top and when scrolling the TableView, the search bar should stay where it is. (Reason for this: in my app there are sometimes a lot of search results, so sometimes the user has to scroll down a while, and when he realizes that he has to change his search string, i don't want to force him to scroll all the way back)
What i already did is adding the search bar to the view of the UINavigationController which is the "parent view" of my table view, instead of adding it to the table view directly:
MyAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
searchBar = [[UISearchBar alloc] init];
searchBar.delegate = self;
[searchBar sizeToFit];
CGFloat searchBarHeight = [searchBar frame].size.height;
CGRect mainViewBounds = delegate.navController.view.bounds;
[searchBar setFrame:CGRectMake(CGRectGetMinX(mainViewBounds),
CGRectGetMinY(mainViewBounds) + 20,
CGRectGetWidth(mainViewBounds),
searchBarHeight)];
searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[delegate.navController.view addSubview: searchBar];
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar: searchBar contentsController: self];
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
... but here my problem is, that the table view stays behind the search bar, and there is always the first TableViewCell of the search results, that is hidden behind my search bar.
Is there a way to resize the UITableView of my UITableViewController permanently, so that it starts right under the search bar?
And there's another problem: Everytime i touch the search bar, the UISearchDisplayController automatically hides the Navigation Bar and resizes the UITableView (it expands to the top), so i think that at this point the UISearchDisplayController will have a big problem with my custom sized TableView...
Thanks a lot for any help!!
I don't have a solution off the top of my head - but I will say the concern you have seems to minimized by the ability for the user to simply tap the top of the screen and have the table zoom to the top (to change search terms if it's wrong).
My general solution for having bits on the page that I don't want to scroll, is to have the UITableView inside another view. That outer view contains two things -- a bar at the top, and a slightly-shorter-than-full-page table below it.
#interface:
UISearchDisplayController < UISearchBarDelegate > *searchDisplayController;
#implementation:
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar: searchBar contentsController: self];
searchDisplayController.delegate = self;
searchBar.delegate = searchDisplayController;
searchDisplayController.searchResultsDataSource = self;
searchDisplayController.searchResultsDelegate = self;
Put this code in your TableViewController:
self.tableView.frame = CGRectMake(0,searchDisplayController?.searchBar.bounds.size.height,320,480);
I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}