In trying to attempt to use Apple's UIViewController containment I have encountered a problem with animating the transition between two UIViewControllers.
Here is the set up... I have created a UITabBarController and within one of the tabs I have created a UIViewController as a container. This ViewController manages the transition between a UIViewController and a UINavigationController. The before view is:
When the Next button is tapped the view begins it's transition with a flipFromRight transition. During the transition, the Navigation bar is in "to" view but is located 20pixels down from the top edge of the view. Picture below:
The green is the background color of the container view. Once the new view completes the transition, the Navigation bar snaps up to the top of the view and the final result is:
The time to snap in place is independent of the duration of the animation. I achieve the final state that I want, but the transition is a problem.
I have instrumented the viewController lifecycle and the frames of the Navigation bar and the UITableView are as specified in the XIB. The xib looks like this:
Here is the code:
In -viewDidLoad -
_fromVC = [[FromVC alloc] initWithNibName:#"FromVC" bundle:nil delegate:self];
[self addChildViewController:_fromVC];
[self.view addSubview:_fromVC.view];
[_fromVC didMoveToParentViewController:self];
In my button handler -
- (void)buttonSelected
{
//
// Create the "to" View controller
//
ToVC *toVC = [[ToVC alloc] initWithNibName:#"ToVC" bundle:nil];
//
// Create the navigation controller for the study activity
//
_toNavCon = [[UINavigationController alloc] initWithRootViewController:toVC];
[self addChildViewController:_toNavCon];
[_fromVC willMoveToParentViewController:nil];
[self transitionFromViewController:_fromVC
toViewController:_toNavCon
duration:0.7
options:UIViewAnimationOptionTransitionFlipFromRight
animations:nil
completion:^(BOOL finished) {
[_fromVC removeFromParentViewController];
[_toNavCon didMoveToParentViewController:self];
}];
}
There is no code in "to" view controller that changes the appearance of the view controller.
Another bit of information... When I "toggle In-Call status bar" in the simulator the gap at the top of the navigation bar is the height of the In-Call status bar.
I have looked at everything on the web and there is nothing that helps. Has anyone seen this and has anyone fixed it?
I did find an answer!! Subclass UINavigationController and override
- (BOOL)wantsFullScreenLayout{
return NO;
}
Apparently, UINavigationController and UITabController always want the full screen (default YES) - adjusting for the status bar - and you can not set this property any other way. It has been working for me.
Related
I am using auto layout with Storyboard. I present a popoverPresentationController from a cell rect:
NumberController * viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"NumberController"];
UIPopoverPresentationController *pc = [viewController popoverPresentationController];
pc.delegate = self;
pc.permittedArrowDirections = UIPopoverArrowDirectionAny;
pc.sourceView = tableView;
pc.sourceRect = [tableView rectForRowAtIndexPath:indexPath];
[self.navigationController presentViewController:viewController animated:animated completion:nil];
The popover presents on an iPad in portrait mode with the arrow up.
I rotate the iPad to landscape mode. The popoverPresentationController keeps the same sourceView/sourceRect and properly points to the cell. It also keeps the up arrow.
But it is now at the bottom of the view, so the popover resizes to a shorter height. This is not desired behavior.
If the popover were simply to move to a new position and change the arrow direction, it would not need to resize at all. This is the desired behavior.
I thought the following method might permit me to make changes, but it is not called since the sourceView rect does not change:
- (void)popoverController:(UIPopoverController *)popoverController
willRepositionPopoverToRect:(inout CGRect *)rect
inView:(inout UIView **)view {
}
I have tried to reset the permittedArrowDirections (in preferredContentSize, because this seemed like the most logical place). This does not work (the popover still resizes):
- (CGSize) preferredContentSize {
[super preferredContentSize];
self.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionUnknown;
return CGSizeMake(DEFAULT_POPOVER_WIDTH,DEFAULT_POPOVER_HEIGHT);
}
I simply cannot find a way to force the popoverPresentationController to change arrow direction and reposition the popover instead of resizing the popover. I am beginning to think it is not even possible - but I still hold out hope that I am just missing something.
EDIT: In the meantime, it has occurred to me that maybe a popover is not the best way to present this view if I don't want it resized in iPad. I am going to try it with UIModalPresentationFormSheet presentation. But I would still like to find an answer to this question.
I just ran into the problem where
- (void)popoverController:(UIPopoverController *)popoverController
willRepositionPopoverToRect:(inout CGRect *)rect
inView:(inout UIView **)view {
was not being called because my view controller was detached. There may be a view in your view hierarchy whose view controller has not been added as a child view controller.
I thought the following method might permit me to make changes, but it is not called since the sourceView rect does not change
The sourceView rect does not have to change, just the interface orientation. From the UIPopoverControllerDelegate documentation:
For popovers that were presented using the presentPopoverFromRect:inView:permittedArrowDirections:animated: method, the popover controller calls this method when the interface orientation changes.
I have a PopoverController view that allows a user to download a file. On button press, the popOver view will expand in size, display download status, and the main view controller will be obscured by an unhidden "cover" view that has been added to the PopoverController's "passThroughViews" property so that the user can not accidentally dismiss the pop over while the file is downloading.
My problem is that, in storyboards, my main viewController is embedded in a Navigation Controller. I can't seem to cover the navigation controller's bar with a view in the storyboard, and if the user presses anywhere on the navigation bar then the popover will disappear and the user will lose the download's progress bar.
How do I either cover up the navigation bar with my "cover" view, or how do I add the navigation bar's view to my popOverController's passThroughViews?
Opening the Popover from the main viewController:
- (IBAction)openDataOptionsPopOver:(id)sender
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
PopOverViewController *optionsWindow = [storyboard instantiateViewControllerWithIdentifier:#"dataOptions"];
self.popUp = [[UIPopoverController alloc] initWithContentViewController:optionsWindow];
[self.popUp setDelegate:self];
[nextNavButton setEnabled:NO]; //Disabling barButtonItem on the navigationController
optionsWindow.containerPopOver = self.popUp; //Pointer to the popover, to resize it later.
optionsWindow.coverView = self.coverView; //Pointer to the coverView, to (un)hide later
[popUp presentPopoverFromRect:[sender frame] inView:[sender superview] permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
}
Setting the passThroughViews property inside of the PopoverViewController:
//Expands the popOver on press of "refreshFileButton" to display progressView
-(void) explodeWindow
{
//setting self.navigationController.view and ...visibleViewController.view here didn't seem to work ...
[containerPopOver setPassthroughViews:[NSArray arrayWithObjects:coverView, nil]];
[containerPopOver setPopoverContentSize:CGSizeMake(600, 400) animated:YES];
[titleBarItem setTitle:#"Downloading File. Please Wait ..."];
[refreshFileButton setHidden:YES];
[progressView setHidden:NO];
[downloadLabel setHidden:NO];
[coverView setHidden:NO];
[progressView setProgress:0.0 animated:NO];
}
I've tried adding self.navigationController.view to passThroughViews with no success--it actually turns out to be a null pointer. And I can't seem to place a UIView at any level in storyboards that will cover all my controls without obscuring the popOver. What am I missing here? And thanks for reading.
Edit:
As Aglaia points out below out, implementing the following, and avoiding passThroughViews, is probably the best way to do this.
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
//Don't dismiss our popover when the view covering our controls is present
if([coverView isHidden]){
return YES;
}else{
return NO;
}
}
Maybe there is something I am missing, but why don′t you just implement a new view controller with its navigation bar set to none and present it modally on button press? Then when the download is finished you just dismiss the view controller.
If you want the user to see the underlying view you can use a UIAlertView instead.
Alternatively set you view controller as the delegate of the popover controller and forbid the user to dismiss your popover on touch outside through
- (BOOL) popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController
{
return NO;
}
Then when you want to dismiss it call dismissPopoverAnimated:
to cover the whole screen including navigation bar:
[myView setFrame:[[UIScreen mainScreen] bounds];
[self.navigationController.view addSubview:myView];
Everything in my app is working a treat - but there is one niggling problem.
The UIImagePickerController seems to return the status bar when it is called. Obviously the app has the statusbar hidden throughout.
Now I have worked around this by rehiding it upon completion or canelation of the picker. This resulted in a black bar at the top of the app. So after the rehide I have had to reposition the titlebar and other table contents for it to fit.
All in all this works perfectly fine. However, the UIImagePickerController is called in detail view of a table. Therefore when the user has used the picker (and ive resized after use) and clicks the back button to return to the main table there is a small graphical glitch.
The detail view has been shifted up to hide the statusbar void, yet when I return to the main table and the app slides horizontally back to the main view, for a split second a 20px black box can be seen above the items on the detail view?
To recap. UIImagePickerController returns the staus bar (seemingly no matter what) and after coding to get rid and reformat the view I get a time (messy) graphical issue when returning to the main view.
Surely there is a way to stop the statusbar returning so I dont have to bodge bar back out using code? I have it set 'off' in the plist.
It very odd! Cheers
This helps to me.
1) You must delegate the UIImagePickerController
2) Add this to ViewController:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{ // Esconder el StatusBar. Provocado por el iOS7 y el UIImagePickerController
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
}
implementing UIimagepicker controller use this.and hide status bar in to plist(statusbar initialy hidden=true) and set Uiview size 320x480 & implementing this
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
{
UIImagePickerController *picker= [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentModalViewController:picker animated:YES];
[picker release];
}
So basically I have this project, where I have 4 different tab bar pages. 2 of them uses navigation controller & tab bar, and 2 are just view controllers. Now there is this one viewcontroller, which I need to add a scroll view to. So basically, I click on the item in the tab bar, it takes me to a view controller, where I can scroll down and up. I have been following Youtube Video Link, but I do not get it to work. The problem is that I use the exact code, and I change the ViewController class to the .h and .m name (ScrollViewViewController), and I put in a scroll view that has 320 x 1000, and a button at the top, but it doesn't scroll! How can I solve this problem?
Note: That if you don't know my problem, but could very detailed walk me through on how to add a scroll view to storyboards in xcode 4.2, then that would be amazing :)!
scrollerViewController.m:
- (void)viewDidLoad {
[scroller setScrollEnabled:YES];
[scroller setContentSize:CGSizeMake(320, 1000)];
[super viewDidLoad];
}
scrollerViewController.h
#interface scrollerViewController : UIViewController {
IBOutlet UIScrollView *scroller;
}
#end
And here comes a picture of the storyboard.
I need some of the code in order to really know your problem, but let me throw in my guess. You may be setting the size 320x1000 to the frame size of the scrollview by any chance? The frame size of the scrollview has to stay 320x480, and the contentSize should be 320x1000. Sorry if it was a wrong guess, but shouldn't do any harm :)
Something like this in your ViewController of the scrollView...
- (void) viewDidLoad {
[super viewDidLoad];
UIScrollView * scrollView = self.view;
scrollView.frame = (CGRect){scrollView.frame.origin, CGSizeMake(320, 480)};
scrollView.contentSize = CGSizeMake(320, 1000);
scrollView.backgroundColor = [UIColor whiteColor]; // I am setting the white background, so that the scroll indicator is visible
}
I am assuming you are setting ScrollView as the top view of the ViewController in your Storyboard as in the attached image.
I followed the exact same movie and it did not work for me either. But this movie did: http://www.youtube.com/watch?v=YBlwepYK7Zs
I followed that one and now i got it to work.
A bit late but might be more people looking for a solution to this.
I have a UINavigationController which contains several UIViewControllers. I would like to create a shared "panel" that is displayed at the top of each view. In addition, I would like to make that panel expand and collapse, overlaying the view, when tapped.
View1
-Top Panel (collapsible)
-Main Panel
View2
-Top Panel (collapsible)
-Main Panel
This would be similar to the way the toolbar or navigation panel hide/show for the camera controller, but it would be a custom view. This is an iPhone app.
As a new XCode developer I would appreciate insights as to how this should be approached from an architectural standpoint.
Create a UIViewController subclass, say called PanelStyleUIViewController,
which would be a superclass for all the views that will have the panel. The superclass implements the panel's controller logic, and the view expansion and contraction, which always happens above the children controller's regular view logic.
This is going to be a modestly difficult project for a new iOS/cocoa developer, because:
You probably will end up having to write a lot of the folding panel's view code programmatically. It is possible to design it in interface builder using more advanced techniques but IB's basic usage is to design one view controller at a time. You need to design a partial, parent view controller that then is inherited to a number of different ones.
You need to make sure that the folding view is always above (z-index wise) the regular view content of the lower level view controller classes. This is something that is probably solved by doing a bringSubviewToFront call on the panel view on, say, the viewDidAppear method.
You are going "off road" from how standard iPhone apps behave. Whenever you do this, you make headaches for yourself and might find yourself at a dead end. My recommendation would be to stay "inside the lines" for a while until you are pretty confident with objective C, cocoa touch, etc.
That said, here's some untested code I wrote here in the stack overflow editor which should give you an idea of what I mean for this superclass design:
// PanelStyleUIViewController.h
#interface PanelStyleUIViewController : UIViewController {
UIView *panelView;
}
#property (nonatomic, retain) UIView *panelView;
// PanelStyleUIViewController.m
#implementation PanelStyleUIViewController
#synthesize panelView;
- (void)viewDidLoad {
[super viewDidLoad];
// setup geometry, contents, etc of panelView programmatically...
self.panelView = [[[UIView alloc] init] autorelease];
panelView.frame = CGRectMake(0,0,320,200);
// set resizing mask appropriately for landscape support
panelView.autoresizingMask = UIViewAutoresizingMaskFlexibleWidth | UIViewAutoresizingMaskFlexibleBottomMargin;
// put a button on the panel view so it can be tapped to slide down
UIButton *slidePanelButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
slidePanelButton.frame = CGRectMake(0,160,320,40);
[slidePanelButton addTarget:self action:#selector(slidePanel) forControlEvents:UIControlEventTouchUpInside];
[panelView addSubview:slidePanelButton];
// set panelView with a transform offset to slide it up under the top of the screen
panelView.transform = CGAffineTransformMakeTranslation(0, -160);
// add panelView to regular view controller's view
[self.view addSubview:panelView];
}
- (void)viewWillAppear {
// make sure the panel shows up on top, z-index wise, since subclasses might have created views that overlap it.
[self.view bringSubviewToFront:panelView];
}
- (void)slidePanel {
// remove the panel transform in an animated fashion (slide down).
// TODO: some button or action needs to slide the panel back up...
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
[panelView setTransform:CGAffineTransformMakeTranslation(0,0)];
[UIView commitAnimations];
}
- (void)viewDidUnload {
[super viewDidUnload];
// always make sure you clean up progammatically-retained views here
self.panelView = nil;
}
- (void)dealloc {
// and here too
self.panelView = nil;
[super dealloc];
}