UIScrollView shifts below navigation and status bar - iphone-sdk-3.0

I have view which contains a scrollView. When the view shows up the image appears in full screen (320x480) hiding the status and navigation bar. When I tap the screens - status and navigation bar appears on the screen. But this thing shifts the UIScrollView below the the navigation bar. I want the status and nav bar to show over my scroll view.
Like it happens in the photos app. On tapping the image the status and nav bar shows over the scrollView.
Below is how I am positioning the view
//What I have done in viewDidLoad
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
self.wantsFullScreenLayout = YES;
scrollView = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = scrollView;
Below is what I am doing on detecting a single tap
- (void)tapDetectingImageView:(TapDetectingImageView *)view gotSingleTapAtPoint:(CGPoint)tapPoint {
// single tap hide/unhide the status and nav bar
NSLog(#"got a single tap");
if (self.navigationController.navigationBarHidden == NO)
{
[self.navigationController setNavigationBarHidden:YES animated:NO];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:YES];
}
else
{
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:YES];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
}
Just a pointer for you guys -
If I use [self.view addSubview:scrollView]; I dont see this issue. But this mess up my landscape orientation code.
Anyone who can shed some light what I might be doing wrong - would be great help!

There appears to be some special handling inside UIScrollView when the navigation toolbars are toggled. As you pointed out at the end of your question, if you put the scroll view inside a containing UIView, this repositioning problem does not occur.
To fix the landscape issues, make sure you're setting the autoresizingMask on both the container view and the scroll view. I did not have to add any additional handling for landscape mode in my project.

I found the solution myself.
In my tap detecting code's else fn I add the below line in the last.
scrollView.contentOffset = CGPointMake(parentScrollView.contentOffset.x,0);
Not sure if this is the right approach. But if anyone can suggest a better approach - more than welcome!

Related

UINavigationBar disappears iOS7

I have some wierd bug with UINavigationBar.
Sometimes it just disappears (actually if you move view to the half of screen, and then just release it)
Video example
In the first ViewController's viewWillAppear: method i call:
[self.navigationController setNavigationBarHidden:NO animated:YES];
The second ViewController's viewWillAppear: contains:
[self.navigationController setNavigationBarHidden:YES animated:NO];
I tried change animated: parameter, but it doesn't help.
Is it iOS7 bug or I just doing something wrong?
I found the reason for this.
That's happened because in info.plist
View controller-based status bar appearance is equal to YES
If change it to NO, then all will be fine
I got the same problem, and fixed it. the solution is:
Modify info.plist, set "View controller-based status bar appearance " to NO;
Delete all - (UIStatusBarStyle)preferredStatusBarStyle {} ;
If your view controller has different status bar style, use [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
e.g. in viewWillAppear set to light, in disappear ,set to dark style.
You should define appearance per navigation controller.
If you want to have a navigation bar on the second controller only you should do the following in that particular controller:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
That way it will event work if you would need to change order of your controllers.

How can a UIViewController inside a UIPageController get touch events?

It seems that the button simply doesn't get pressed.
I tried:
UIPageViewController Gesture recognizers
However, here is the catch
if page transition of UIPageViewController is UIPageViewControllerTransitionStyleScroll then
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
NSLog(#"%#",self.pageViewController.gestureRecognizers.description);
NSLog(#"%#",self.pageViewController.gestureRecognizers.description);
will simply be empty.
I tried adding my own gestureRecoqnizers instead of button into the UIViewControllers's subview. It still doesn't work.
So I have a UIPageViewController that display a UIViewController whose view has some button. How do I get that button pressed?
I also tried both solutions here:
http://emlyn.net/posts/stopping-gesture-recognizers-in-their-tracks
None works. First of all I cannot disable gesture recoqnizers of the UIPageViewController because there is no such thing. In scrollView mode, UIPageViewController doesn't have gesture recoqnizers.
I tried to add my own gesture recoqnizers to my own button but those are never called.
I want the UIPageViewController to still handle swipe though. But not tap.
I can't access the gesture recoqnizers of UIPageViewControllers because self.pageViewController.gestureRecognizers is empty. The UIPageViewController seems to just "absorb" all tap events. So my button doesn't get it.
I can add button in front of UIPageViewController but that button will absorb the swiping action which I want UIPageVIewController to still handle.
By the way if we uses the template from IOS (Create a page based app, and change the transition to scroll the pageviewcontroller.gesturerecoqnizers will be empty
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Configure the page view controller and add it as a child view controller.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];//Turn this to scroll view
self.pageViewController.delegate = self;
SDDataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
pageViewRect = CGRectInset(pageViewRect, 40.0, 40.0);
}
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
NSLog(#"%#",self.pageViewController.gestureRecognizers.description); //This is empty
while (false);
}
Maybe you can set up a custom gesture recognizer in your UIPageViewController. This way, you will be able to filter through UITouch Events, by implementing this in your gesture recognizer delegate (the one for your UIPageViewController) :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch {
if (touch.tapCount > 1) { // It will ignore touch with less than two finger
return YES;
}
return NO;
}
This, you will tell the UIPageViewController gestureRecognizer to not care about touch with one finger. You'll have to try it, but i think it might do the trick. The problem here is you have to ask your user for a two-finger swipe...
Check here for an other answer

Presenting a modal view controller with cross-disolve isn't showing views added in code

When I present a modal view controller over my view storyboard originated root view controller it flashes the story board view, even if it has been altered. My root view controller has a bright green back ground with large white subview across the top that contains a label bound to an IBOutlet. In UIViewController viewDidLoad I am adding a light gray, slightly transparent UIView that covers the entire view as a subview of the viewcontroller's view. I am also setting the label text to be different than that of the Storyboard layout.
When I trigger the modal, either via segue or via presentViewController:completion: what I see is the light gray view over my label then the view added in code seems to become transparent and animate away and the bright green of the storyboard layout shows up and then the modal view cross-dissolves in. When I dismiss the first and present the second in the completion block i see a cross-dissolve to bright green, pop of light as the added view becomes visible again, pop to bright green and the cross-dissolve in of the second view controller's view.
When I dismiss the second view controller then I see cross-dissolve out to the bright green and then the added light gray, semi-transparent view pops back to being visible.
Anyone have any idea how to stop the cross-dissolve from showing the views underneath the view added in code?
This example is made more jarring if you remove the opacity from the overlay view added in viewDidLoad.
https://github.com/jonnolen/ios-cross-disolve-problem
Code snippet and story board layout:
#interface DTViewController (){
BOOL hasShownSegue;
}
#end
#implementation DTViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView * view = [UIView new];
view.frame = self.view.bounds;
view.backgroundColor = [UIColor colorWithWhite:.95 alpha:.7];
NSLog(#"View Bounds: %#", NSStringFromCGRect(self.view.bounds));
[self.view addSubview:view];
self.label.text = #"Hello!";
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (!hasShownSegue){
[self performSegueWithIdentifier:#"modal.1.segue" sender:self];
hasShownSegue = YES;
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
DTCallbackViewController *vc = segue.destinationViewController;
vc.completionCallback = ^{
[self dismissViewControllerAnimated:YES completion:^{
DTCallbackViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:#"vc.2"];
vc.completionCallback = ^{
[self dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:vc animated:YES completion:nil];
}];
};
}
Figured it out, I don't really like it but it works:
I put all of the maskable content into a separate view:
And then hide that view when I need it to be "masked".
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"View Bounds: %#", NSStringFromCGRect(self.view.bounds));
self.label.text = #"Hello!";
self.content.hidden = YES;
}
Then animations behave as expected. However, this only works with an opaque mask (which is what I needed).

Change appearance of UINavigationBar from solid to transparent

I want to achieve the exact same effect as the Photos app on the iPad: solid black navigation bar during the gallery controller, then transparent when viewing a photo.
After I initialize my navigation controller in the AppDelegate I can change the style using the below code, but not outside the AppDelegate. However once the navigation controller is on screen and set once, navigationBar becomes a read-only property.
I would greatly appreciate if someone can share how this is done. Thanks.
self.navigationController.navigationBar.tintColor = [UIColor clearColor];
self.navigationController.navigationBar.translucent = YES;
self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
UINavigationBar in UIBarStyleBlackTranslucent barStyle doesn't fix it's place as it normally does in other styles. In UIBarStyleBlackTranslucent style it just overlaps on your view and contents of views are translated up and view height is also increased.
if you're showing photos then first hide your navigationBar :
[[self navigationController] setNavigationBarHidden:YES animated:NO];
Now set your navBar's style to UIBarStyleBlackTranslucent
[self.navigationController.navigationBar setBarStyle:UIBarStyleBlackTranslucent];
Then show your imageview (or scrollview with picture- depends on your logic). Then on tapping on your imageview you can show and hide bar)
// Show/Hide bar
// Let say you've a bool 'shown' for current status of navbar's visibility.
if (!shown) {
[[self navigationController] setNavigationBarHidden:YES animated:YES];
}else{
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}
shown = !shown;
Hope this'll do it. ANd never forget to rechnage barStyle to UIBarStyleBlackOpaque once you exit gallery.
Seems like you want to display navigationBar at the Gallery so there no need to change anything but after selecting a photo, keep the NavigationBar hidden in the PhotoViewController.
-(void) viewDidLoad
{
self.navigationController.navigationBar.barStyle=UIBarStyleBlackTranslucent;
self.navigationController.navigationBar.hidden = NO;
}
and in ViewWillAppear
-(void) viewWillAppear
{
self.navigationController.navigationBar.hidden = YES;
}
And then create a custom method implementing UITapGestureRecognizer where on a single tap display the navigationBar by setting hidden property to YES.

Presenting a Modal View Controller hides the Navigation Bar

I have a navigation based app with a navigation bar, but there are a few instances where instead of pushing a view controller onto the stack, I need to present the view controller modally. The problem is that when I dismiss the modal view controller, everything functions as expected except that the navigation bar is hidden and the (parent view) has been resized, which is the expected behavior according to the docs. So I figured I could simply call a built-in method to unhide the navigation bar. I have already tried
[self.navigationController setNavigationBarHidden:NO];
as well as the animated version without success.
The documentation talks about this in the method
presentModalViewController: animated:
in the discussion section where it says,
On iPhone and iPod touch devices, the view of modalViewController is always presented full screen" and "Sets the modalViewController property to the specified view controller. Resizes its view and attaches it to the view hierarchy."However, the docs didn't clue me in as to how to undo this process after dismissing a modal view.
Has anyone else experienced this and found a solution?
Edit: I am having this same problem, so instead of asking my own question I am sponsoring a bounty on this one. This is my specific situation:
In my case, I am presenting an Image Picker in a Modal View Controller, over a Navigation Controller:
-(void) chooseImage {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
imagepicker = [[UIImagePickerController alloc] init];
imagepicker.allowsEditing = NO;
imagepicker.delegate = self;
imagepicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagepicker.navigationBar.opaque = true;
imagepicker.wantsFullScreenLayout = NO;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if (self.view.window != nil) {
popoverController = [[UIPopoverController alloc] initWithContentViewController:imagepicker];
[popoverController presentPopoverFromBarButtonItem:reset permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
} else {}
} else {
[self.navigationController presentModalViewController:imagepicker animated:YES];
}
}
}
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.popoverController dismissPopoverAnimated:true];
} else {
[self.navigationController dismissModalViewControllerAnimated:YES];
}
//Save the image
}
-(void) imagePickerControllerDidCancel:(UIImagePickerController *)picker {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.popoverController dismissPopoverAnimated:true];
} else {
[self.navigationController dismissModalViewControllerAnimated:YES];
}
}
Make sure you a presenting AND dismissing the modalViewController from the UINavigationController, like so:
// show
[self.navigationController presentModalViewController:vc animated:YES];
// dismiss
[self.navigationController dismissModalViewControllerAnimated:YES];
If your view controller is actually on the UINavigationController's stack then this is the correct way to handle the presentation and dismissal of the modal view controller. If your UINavigationBar is still hidden, there is something else funky going on and we would need to see your code to determine what is happening.
Edit
I copied your code into an app of mine and the UIImagePickerController successfully presented and dismissed and my UINavigationController's UINavigationBar was still there. I truly believe that the problem lays elsewhere in your architecture. If you upload a zip w/ an example project I will take a look.
Simply try following code it will work
SettingsViewController *settings = [[SettingsViewController alloc] init];
UINavigationController *navcont = [[UINavigationController alloc] initWithRootViewController:settings];
[self presentModalViewController:navcont animated:YES];
[settings release];
[navcont release];
One need to present the navigation controller in order to have navigation bar on the presented controller
I think I've seen this behavior when presenting a view controller on the wrong VC. Are you calling presentModalViewController on the navigation controller or the individual VC?
Try calling it from the navigationController if you aren't already.
[self.navigationController presentModalViewController:myVC animated:YES];
If you present a controller as model, View controller will appear to total view.
If you want to access the navigation controller properties over the model view, You need to create another navigation controller reference and it continues as previous.
This may be useful for you.
Check this out. This is Apple's Documentation under UIViewController Class Reference:
It clearly mentions that modal view always presents in full screen mode, so it is obvious that navigation bar will be hidden. So put the seperate navigation bar on modal view to navigate back.
presentModalViewController:animated:
Presents a modal view managed by the given view controller to the user.
- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
Parameters
modalViewController
The view controller that manages the modal view.
animated
If YES, animates the view as it’s presented; otherwise, does not.
Discussion
On iPhone and iPod touch devices, the view of modalViewController is always presented full screen. On iPad, the presentation depends on the value in the modalPresentationStyle property.
Sets the modalViewController property to the specified view controller. Resizes its view and attaches it to the view hierarchy. The view is animated according to the transition style specified in the modalTransitionStyle property of the controller in the modalViewController parameter.
Availability
Available in iOS 2.0 and later.
Hope this helps you understand that hiding the whole view along with navigation controller is default behaviour for modal view so try putting a seperate navigation bar in modal view to navigate.
You can check it further on this link
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
AddContactVC *addController =[self.storyboard instantiateViewControllerWithIdentifier:#"AddContactVC"];
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:addController];
[self presentViewController:navigationController animated:YES completion: nil];
working for me shows navigation bar
Emphatic and Devin –
As I started reading through the Apple docs to get familiar with the problem, I noticed that the method you're using, presentModalViewController:animated:, appears to be deprecated in favor of presentViewController:animated:completion:. Perhaps you should try to use that method instead.
For your convenience, take a look for yourself:
presentModalViewController:animated: reference
I'll try to put together a quick test program to see whether what I've said above is actually true. But give it a shot – maybe it'll help!
Xcode has a template that does pretty close to what you're doing. from the results, i don't think you should be attempting to perform [self.navigationController presentModalViewController:vc] and [self.navigationController dismissModalViewControllerAnimated:] , but rather simply [self presentModalViewController:] and [self dismissModalViewControllerAnimated:] .
to see how the template does this for yourself, you can use the new project wizard in xcode 4.3 . perhaps it will provide some guidance:
from that choice, choose Next, then give your test project a name, choose "Universal", turn off automatic reference counting, hit next, save where you want it.
now, click on the target and switch the deployment target to 4.3 (or 4.0 if you prefer) for your testing purposes, and switch to your device or the iOS 4.3 simulator .
finally, substitute the following code in applicationDidFinishLaunching:withOptions: in the created AppDelegate.m:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.mainViewController = [[[MainViewController alloc] initWithNibName:#"MainViewController_iPhone"
bundle:nil] autorelease];
} else {
self.mainViewController = [[[MainViewController alloc] initWithNibName:#"MainViewController_iPad"
bundle:nil] autorelease];
}
UINavigationController* navigationController
= [[UINavigationController alloc] initWithRootViewController:self.mainViewController];
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
now, when i run this, it doesn't hide the navigationBar. and in the created MainViewController.m from the template, you'll see how it presents the modal view controller and dismisses it from the controller itself and not from the navigation controller. for good measure, to make the template code more like your own, go into MainViewController.m and delete the line that sets the modal view controller transition style ...
(of course, in iOS 5, with storyboards, the same thing can all be accomplished with modal segues ... which is how i've done this for apps that i'm not supporting for pre-5.0 that present a modalViewController in this fashion.)
One of the best solution it to use this Category MaryPopin
https://github.com/Backelite/MaryPopin