Transparent status bar for OS-supplied view controllers - ios

My app allows users to import video and perform a few other actions like send an email. When I call the standard code to present these view controllers (example below), the result is that scrollable elements appear underneath the status bar, which is hideous. My own View Controllers do not suffer from this glitch. The problem seems to occur in iOS 7 and 8.
I see many related questions, but most of them address dealing with the status bar in your own view controllers, and I've already done that. My question is about dealing with view controllers I get from the OS.
Any idea what we might be doing (e.g. in storyboard) to cause this? Did I forget or miss an obvious step? It's very ugly and distracting and a fix would be fantastic. I don't see this in any other app.
UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
mediaUI.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
// Displays saved movies from the Camera Roll album.
mediaUI.mediaTypes = #[(__bridge NSString *)kUTTypeMovie];
mediaUI.allowsEditing = YES;
mediaUI.delegate = self; //with or without this, the glitch appears
[self presentViewController:mediaUI animated:YES completion:nil];
Here's what it looks like:

The only solution I could find was to remove the status bar. This might be possible via davidisdk's answer to this question, but I did it by subclassing UIImagePicker (and a few other classes) like this:
#interface NoStatusBarImagePickerController : UIImagePickerController
#end
#implementation NoStatusBarImagePickerController
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return nil;
}
#end

Related

How to prevent the UISearchController from showing the navigation bar?

My use case is kind of strange. I'm using my own navigation bar, so I hide the default one with
[self.navigationController setNavigationBarHidden:YES animated:NO];
on viewWillAppear.
Everything works great, but if I have the keyboard open on an active search, and then I go back to a previous UIViewController, then the native navigation bar shows up again, and I end up with 2 navigation bars (my own and the default).
This is how I'm setting up my UISearchController in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.obscuresBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
[self.searchController.searchBar sizeToFit];
self.definesPresentationContext = YES;
self.searchController.hidesNavigationBarDuringPresentation = YES;
self.searchController.searchBar.searchBarStyle = UISearchBarStyleMinimal;
}
I already tried removing the self.definesPresentationContext = YES; (or turning it to NO), but that creates a different issue, which is that when I go back to the previous UIViewController the search bar stays on top of everything else! Until I tap on Cancel. I also tried calling the Cancel button programatically on viewWillDissappear, but that didn't work either...
So I'm running out of options, and that's why I'm here.
Any thoughts?
Using a navigation bar that is not the standard maybe it's not the best idea. (I know customers can sometimes be stubborn, but we should teach them that sometimes standard solutions have a lot of good points, like low maintenance for example, which turns in lower bills for them).
Having said that, as a last resort I may suggest you a quite "strong" approach.
You could subclass the UINavigationController with a custom class, and inside this class you could override the setNavigationBarHidden method like this
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated{
[super setNavigationBarHidden:YES animated:NO];
}
method. This should make the bar hidden all the time.
Still, i'm not a big supported of this kind of solutions, but it may work in your case.

addSubview not calling QLPreviewControllerDataSource instances when offline

I have the following piece of code to add a QLPreviewController subview
{
QLPreviewController *preview = [[QLPreviewController alloc] init];
preview.delegate = self;
preview.dataSource = self;
[self addChildViewController:preview];
[self.view addSubview:preview.view];
[preview didMoveToParentViewController:self];
self.previewController = preview;
}
-(NSInteger) numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller
{
return 1;
}
-(id) previewController:(QLPreviewController *)previewController previewItemAtIndex:(NSInteger)index
{
return self.url;
}
self.url is an NSURL that is located in NSTemporaryDirectory - file://localhost//.../blah.pdf
My issue is that when my laptop is connected to the internet, the document shows up as a subview, but when my laptop is not connected, numberOfPreviewItemsInPreviewController & previewItemAtIndex do not get called.
I've tried a vanilla program with a simple view controller, and it seemed to work fine. (My app is more complex than that).
When I try presenting the document as a modal view, it seems to work irrespective of whether or not the simulator is connected to the internet.
[self presentViewController:preview animated:NO completion:nil]; --> works consistently.
I need to get the subview working for online & offline modes, it would be great if someone could help!
You might be encountering strange behaviour because the QLPreviewController's view is not designed to be embedded in another view. From the QLPreviewController class reference overview:
To display a Quick Look preview controller you have two options: You can push it into view using a UINavigationController object, or can present it modally, full screen, using the presentModalViewController:animated: method of its parent class, UIViewController.
Having said that, you could try:
Forcing the QLPreviewController to (re)display its contents. Try adding [self.previewController reloadData]; to the end of your first method. This should force the data source method(s) to fire.
Forcing the view to "refresh" it's subviews: [self.view setNeedsLayout] (which may in fact force a reloadData like the first option).
Good luck!

strange space issue when adding Kal as subview

There's too much black space between the Kalendar and the navigation bar. Here's a picture of what I'm talking about:
I'm calling the code from ViewDidLoad of a ViewController that I have in my storyboard:
- (void) viewDidLoad {
KalViewController *calendar = [[KalViewController alloc] init];
[self.view addSubview:calendar.view];
}
I'm just learning to use Kal. Maybe I'm going about incorporating it in the wrong way. I sure do like using the storyboard to link up my views so my viewcontroller includes "Kal.h" and I instantiate as shown in the code above. I tried to make the viewcontroller a subclass of KalViewController but that didn't work.
Maybe there's a more elegant way of incorporating Kal into a hierarchy of storyboarded view controllers?
Just add the following:
[[self navigationController] initWithRootViewController:calendar];
[[calendar navigationItem] setHidesBackButton:YES];
and it should do the trick. The second line is being used to hide the "Back" button that otherwise becomes visible, but for me it's a single screen app, you may or may not want to use it.
I used the exact same code as you initially did ACB, had the exact same problem but I also had included [self addChildViewController:calendar]; after the creation of the calendar view.
What does this code do? [self addChildViewController:calendar];
I understand it "adds a child view controller" but what is REALLY going on.... and should I need this? Apparently not because you didn't, and I just compiled my code without it and there was no difference.
Overall code:
KalViewController *calendar = [[KalViewController alloc] init];
[self.view addSubview:calendar.view];
//[self addChildViewController:calendar];

How to use a UISplitViewController as a Modal View Controller?

I am trying to display a UISplitViewController presenting it as a Modal View Controller in my iPad app. I manage to have it display, but for some reason there is a gap to the left of the modal view the size of a Status Bar which is also preserved when the orientation is changed.
Does anybody know why this is happening? Or if this is even possible? Maybe I'm just digging myself a huge hole.
Like for many of you, I needed a 'modal way' to use the UISplitViewController. This seems to be an old issue, but all I found in StackOverflow was at best an explanation why the problem happens when you attempt to do so (like the accepted answer above), or 'hack-arounds'.
However, sometimes it is also not very convenient to change much of your code-base and make a UISplitViewController the initial object just to get it's functionality up and running.
In turns out, there's a way to make everybody happy (including Apple guidelines). The solution that I found best, was to use the UISplitViewController normally, but when needed to be shown/dismissed, use the following approach:
-(void)presentWithMasterViewController: (UIViewController *) thisMasterViewController
andDetailViewController: (UIViewController *) thisDetailViewController
completion:(void(^)(void))completion
{
masterViewController = thisMasterViewController;
detailViewController = thisDetailViewController;
[self setViewControllers:[NSArray arrayWithObjects:masterViewController, detailViewController, nil]];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
self.window.rootViewController = self;
[self.window makeKeyAndVisible];
if(completion)
completion();
}
-(void)dismissViewControllerWithCompletion:(void (^)(void))completion
{
self.window = nil;
masterViewController = nil;
detailViewController = nil;
if(completion)
completion();
}
Where "window", is a property of your UISplitViewController subclass. And the system will take care of the rest!
For convenience/reference, I uploaded this as a UISplitViewController subclass to gitHub:
ModalSplitViewController
--EXAMPLE ON HOW TO USE --
mySplitViewController = [[ModalSplitViewController alloc] init];
mySplitViewController.delegate = self;
[mySplitViewController presentWithMasterViewController:masterViewController andDetailViewController:detailViewController completion:nil];
// when done:
[mySplitViewController dismissViewControllerWithCompletion:nil];
mySplitViewController = nil;
Side-note: I guess most of the confusion originates from the fact that
the UISplitView usage example from Apple documentation uses the window
created in the appDelegate, and for the fact that most people are not
so familiar with the window concept - because we normally don't need
to (they are created once in StoryBoards or boilerplate code).
Additionally, if you are doing state restoration, one should not
forget that programmatically-created UIViewControllers won't
automatically be restored by the system.
The stock UISplitViewController was designed for use as the root view controller only. Presenting one modally goes against the Apple Human Interface Guidelines and has a high probability of getting rejected by the App Review Team. In addition, you may receive the error:
Application tried to present Split View Controllers modally
Technically, this is what I did:
1/ Subclass a UIViewController ie. #interface aVC: UIViewController
2/ In the viewDidLoad, set up a splitViewController, ie. aSplitVC
3/ Then self.view = aSplitVC.view
After all, present aVC as modalViewController
I agree with Evan that this is slightly off-color for Apple, but I was able to complete a working version of this with the following solution:
UISplitViewController *splitVC = [[UISplitViewController alloc] init];
splitVC.delegate = VC2;
splitVC.viewControllers = [NSArray arrayWithObjects:navcon1, navcon2, nil];
UINavigationController *splitNavCon = [[UINavigationController alloc] init];
splitNavCon.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[splitNavCon.view addSubview:splitVC.view];
VC2.splitParentViewController = splitNavCon;
[self presentViewController:splitNavCon animated:YES completion:nil];
This allowed me to have a working back button in the new UISplitViewController that was presented modally on the screen.
You'll notice that I actually pass the VC2 (the delegate of the UISplitViewController) its parent UINavigationController. This was the best way that I found I could dismiss the UISplitViewController from within the VC2:
[splitParentViewController dismissViewControllerAnimated:YES completion:nil];
I believe one can do the other way around: instead of custom controller presenting split controller, one can set up the split controller as the root window controller in storyboard, and on top of its view you can add your custom controller (ie, login screen) and remove it from the screen (removeFromSuperview for example) when it is needed.
That answer is not actually correct, because it not valid any longer since iOS8 and if you need to support even iOS7 you can do that like you put actually modally UIViewController which has a container as SplitView.
let mdSplitView = self.storyboard?.instantiateViewControllerWithIdentifier("myDataSplitView") as! MyData_SplitVC
self.addChildViewController(mdSplitView)
mdSplitView.view.bounds = self.view.bounds
self.view.addSubview(mdSplitView.view)
mdSplitView.didMoveToParentViewController(self)

UITextField resignFirstResponder, but Keybord will not disappear [duplicate]

Note:
See accepted answer (not top voted one) for solution as of iOS 4.3.
This question is about a behavior discovered in the iPad keyboard, where it refuses to be dismissed if shown in a modal dialog with a navigation controller.
Basically, if I present the navigation controller with the following line as below:
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
The keyboard refuses to be dismissed. If I comment out this line, the keyboard goes away fine.
...
I've got two textFields, username and password; username has a Next button and password has a Done button. The keyboard won't go away if I present this in a modal navigation controller.
WORKS
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
[self.view addSubview:b.view];
DOES NOT WORK
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
UINavigationController *navigationController =
[[UINavigationController alloc]
initWithRootViewController:b];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
navigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];
If I remove the navigation controller part and present 'b' as a modal view controller by itself, it works. Is the navigation controller the problem?
WORKS
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
b.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:b animated:YES];
[b release];
WORKS
broken *b = [[broken alloc] initWithNibName:#"broken" bundle:nil];
UINavigationController *navigationController =
[[UINavigationController alloc]
initWithRootViewController:b];
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];
This has been classified as "works as intended" by Apple engineers. I filed a bug for this a while back. Their reasoning is that the user is often going to be entering data in a modal form so they are trying to be "helpful" and keep the keyboard visible where ordinarily various transitions within the modal view can cause the keyboard to show/hide repeatedly.
edit: here is the response of an Apple engineer on the developer forums:
Was your view by any chance presented with the UIModalPresentationFormSheet style? To avoid frequent in-and-out animations, the keyboard will sometimes remain on-screen even when there is no first responder. This is not a bug.
This is giving a lot of people problems (myself included) but at the moment there doesn't seem to be a way to work around it.
UPDATE:
In iOS 4.3 and later, you can now implement `-disablesAutomaticKeyboardDismissal' on your view controller to return NO:
- (BOOL)disablesAutomaticKeyboardDismissal {
return NO;
}
This fixes the issue.
Be careful if you are displaying the modal with a UINavigationController. You then have to set the disablesAutomaticKeyboardDismissal on the navigation controller and not on the view controller. You can easily do this with categories.
File: UINavigationController+KeyboardDismiss.h
#import <Foundation/Foundation.h>
#interface UINavigationController (KeyboardDismiss)
- (BOOL)disablesAutomaticKeyboardDismissal;
#end
File: UINavigationController+KeyboardDismiss.m
#import "UINavigationController+KeyboardDismiss.h"
#implementation UINavigationController(KeyboardDismiss)
- (BOOL)disablesAutomaticKeyboardDismissal
{
return NO;
}
#end
Do not forget to import the category in the file where you use the
UINavigationController.
In the view controller that is presented modally, just override disablesAutomaticKeyboardDismissal to return NO:
- (BOOL)disablesAutomaticKeyboardDismissal {
return NO;
}
I solved this by using the UIModalPresentationPageSheet presentation style and resizing it immediately after I present it. Like so:
viewController.modalPresentationStyle = UIModalPresentationPageSheet;
viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:viewController animated:YES];
viewController.view.superview.autoresizingMask =
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin;
viewController.view.superview.frame = CGRectMake(
viewController.view.superview.frame.origin.x,
viewController.view.superview.frame.origin.y,
540.0f,
529.0f
);
viewController.view.superview.center = self.view.center;
[viewController release];
If you toggle a different modal display you can get the keyboard to disappear. It's not pretty and it doesn't animate down, but you can get it to go away.
It'd be great if there was a fix, but for now this works. You can wedge it in a category on UIViewController and call it when you want the keyboard gone:
#interface _TempUIVC : UIViewController
#end
#implementation _TempUIVC
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#end
#implementation UIViewController (Helpers)
- (void)_dismissModalViewController {
[self dismissModalViewControllerAnimated:NO];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
[self release];
}
- (void)forceKeyboardDismissUsingModalToggle:(BOOL)animated {
[self retain];
_TempUIVC *tuivc = [[_TempUIVC alloc] init];
tuivc.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:tuivc animated:animated];
if (animated) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(_dismissModalViewController) name:UIKeyboardDidHideNotification object:nil];
} else
[self _dismissModalViewController];
[tuivc release];
}
#end
Be careful with this though as you viewDidAppear / viewDidDisappear and all those methods get called. Like I said, it's not pretty, but does work.
-Adam
You could also work around this in a universal app by simply checking the idiom and if it's an iPad, don't pop up the keyboard automatically at all and let the user tap whatever they want to edit.
May not be the nicest solution but it's very straightforward and doesn't need any fancy hacks that will break with the next major iOS release :)
Put this code in your viewWillDisappear: method of current controller is another way to fix this:
Class UIKeyboardImpl = NSClassFromString(#"UIKeyboardImpl");
id activeInstance = [UIKeyboardImpl performSelector:#selector(activeInstance)];
[activeInstance performSelector:#selector(dismissKeyboard)];
I found that disablesAutomaticKeyboardDismissal and adding a disablesAutomaticKeyboardDismissal function didn't work for my UITextField in a modal dialog.
The onscreen keyboard just wouldn't go away.
My solution was to disable all text-input controls in my dialog, then re-enable the relevant ones a fraction of a second later.
It seems as though when iOS sees that none of the UITextField controls are enabled, then it does get rid of the keyboard.
I'm sure you have looked at this, but you are sure that your controller class is properly hooked up as the UITextField delegate, right?
maybe don't return NO, but YES. So it can go away.
And you have a textFieldShouldEndEditing returning YES as well?
And why are you firing [nextResponder becomeFirstResponder]?! sorry i see now
I also have a number of UITextViews
which all have their "editable"
property set to FALSE.
May we assume none of them, by any chance, has a tag value of secondField.tag+1? If so, you're telling them to become first responder, instead of resigning the first responder. Maybe put some NSLog() in that if structure.
For those having trouble with UINavigationController, see my answer to a similar question here:
https://stackoverflow.com/a/10507689/321785
Edit:
I consider this an improvement to Miha Hribar's solution (since the decision is taking place where it should), and as opposed to Pascal's comment regarding a category on UIViewController
may be not a perfect solution ,but works
[self.view endEditing:YES];
from wherever your button or gesture is implemented to present modal
Swift 4.1:
extension UINavigationController {
override open var disablesAutomaticKeyboardDismissal: Bool {
return false
}
}

Resources