I want to present a modal view controller. In iOS7 everything runs fine but in iOS8 the frame of view controller is changed. I read some answers and one of the solutions is to set the preferredContentSize and the modalPresentationStyle = UIModalPresentationFormSheet.
But i need my modalPresentationStyle = UIModalPresentationCustom and i can't set the frame of view controller with this presentation style.
My code is:
- (void)presentViewController:(UIViewController*)viewController withCustomSize:(NSValue*) size withInitialSetup:(void (^)(void))setupBlock withCompletion:(void (^)(void))completion {
UiViewController * navCon = [[UIViewController alloc] initWithRootViewController:viewController];
navCon.shouldDismissKeyboardOnResign = YES;
if (SYSTEM_VERSION_LESS_THAN(IOS8)){
navCon.modalPresentationStyle = UIModalPresentationFormSheet;
navCon.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
} else {
navCon.modalPresentationStyle = UIModalPresentationCustom;
navCon.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
}
[viewController setupCustomNavigationBar];
setupBlock();
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(IOS8)) {
navCon.transitioningDelegate = self;
if (size) {
CGSize sz = size.CGSizeValue;
navCon.preferredContentSize = CGSizeMake(sz.width, sz.height);
}else {
navCon.preferredContentSize = CGSizeMake(kPopupsWidth, kPopupsHeight);
}
}
[self presentViewController:navCon animated:YES completion:completion];
if (SYSTEM_VERSION_LESS_THAN(IOS8)) {
if (size) {
CGSize sz = size.CGSizeValue;
navCon.view.superview.bounds = CGRectMake(0, 0, sz.width, sz.height);
} else {
navCon.view.superview.bounds = CGRectMake(0, 0, kPopupsWidth, kPopupsHeight);
}
[navCon.view.superview.layer setCornerRadius: 8];
[navCon.view.superview.layer setBorderColor: [UIColor clearColor].CGColor];
[navCon.view.superview.layer setBorderWidth: 2];
[navCon.view.superview setClipsToBounds: YES];
}
}
I set the preferedContentSize but the frame doesn't change. Any idea why this happens?
If you're going to use UIModalPrestationCustom you have to provide a UIViewControllerTransitioningDelegate, otherwise its going to use the default transitioning delegate, which in your case sounds like a form sheet delegate.
Related
i am using UIPresentationController for custom modal presentation for menu
this part is fine , let say i have clicked 'feedback' i am presenting another controller on top of it .
the problem is after dismissing feedback controller the size of menu view become full
my presentation controller class is below
#import "MRMenuPresentationController.h"
#implementation MRMenuPresentationController
#synthesize dimmingView;
-(void)presentationTransitionWillBegin
{
UITapGestureRecognizer * theTapGesture = [UITapGestureRecognizer new];
theTapGesture.numberOfTapsRequired = 1;
theTapGesture.numberOfTouchesRequired = 1;
[theTapGesture addTarget:self action:#selector(handleTap:)];
dimmingView = [UIView new];
[dimmingView addGestureRecognizer:theTapGesture];
dimmingView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
self.dimmingView.frame = self.containerView.bounds;
self.dimmingView.alpha = 0.0;
[self.containerView addSubview:dimmingView];
[self.containerView addSubview:self.presentedView];
// Fade in the dimming view alongside the transition
id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
[transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
self.dimmingView.alpha = 1.0;
} completion:nil];
}
-(void)presentationTransitionDidEnd:(BOOL)completed
{
if (!completed) {
[self.dimmingView removeFromSuperview];
}
}
-(CGRect)frameOfPresentedViewInContainerView
{
CGRect frame = self.containerView.bounds;
frame.size.width = frame.size.width - 50 ;
return frame;
}
-(void)dismissalTransitionWillBegin
{
id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
[transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
self.dimmingView.alpha = 0.0;
} completion:nil];
}
-(void)dismissalTransitionDidEnd:(BOOL)completed
{
if (completed) {
[self.dimmingView removeFromSuperview];
}
}
please tell what i am missing
Thanks in advance
For some reason I'm having trouble displaying a view from a secondary view controller in my application.
My main view controller calls another view controller that's responsible for loading a pdf view.
The code in MainViewController looks like this:
- (int)openPDF
{
[self loadSettingsWithDefaults];
RDPDFViewController *m_pdf;
if( m_pdf == nil )
{
m_pdf = [[RDPDFViewController alloc] initWithNibName:#"RDPDFViewController"bundle:nil];
}
int result = [m_pdf PDFOpen:#"/Users/steve/test.pdf" withPassword:#""];
if(result == 1)
{
m_pdf.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:m_pdf animated:YES];
}
return result;
}
This code is located in the second view. This is the method referred to above in MainViewController.
- (int)PDFOpen:(NSString *)path withPassword:(NSString *)pwd {
[self PDFClose];
PDF_ERR err = 0;
m_doc = [[PDFDoc alloc] init]; err = [m_doc open:path :pwd]; switch( err )
{
case err_ok: break;
case err_password: return 2;
break; default: return 0;
}
CGRect rect = [[UIScreen mainScreen]bounds];
//GEAR
if (![self isPortrait] && rect.size.width < rect.size.height) { float height = rect.size.height;
rect.size.height = rect.size.width;
rect.size.width = height;
}
//END
if(SYS_VERSION>=7.0) {
m_view = [[PDFView alloc] initWithFrame:CGRectMake(0, 0, rect.size.width, rect.size.height)]; }
else
{
m_view = [[PDFView alloc] initWithFrame:CGRectMake(0, 0, rect.size.width, rect.size.height-20-44)];
}
[m_view vOpen :m_doc :(id<PDFViewDelegate>)self];
pagecount = [m_doc pageCount];
[self.view addSubview:m_view];
return 1; }
When the code executes, I know the view object is being created as I can see debug output coming from it in the console. However it will not display the new view in the iPhone simulator.
Feel free to provide feedback--just remember this is my first StackOverflow post! I'll be happy to provide more info as needed.
My main view controller was set to be the root view controller for my application. I had to set the navigation controller to be the root view controller, and then add the other view controllers after that.
After that I told navController to pop to the root view controller.
navController = [[UINavigationController alloc] initWithRootViewController:mainViewController];
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
[navController setNavigationBarHidden:YES];
[navController popToRootViewControllerAnimated:YES];
I present a small "login" UIViewController as a UIModalPresentationFormSheet with custom bounds. In the viewWillLayoutSubviews method, I change the size of the view to (300,250). This has worked in iOS 5/6/7 but no longer works in 8.
When the view is presented and a UITextField is tapped, the app becomes unresponsive (not frozen, just not responding to touches). Almost as if the keyboard is presented but not appearing. Delegate methods ARE called correctly. If I remove the self.view.superview.bounds = CGRectMake(0, 0, 300, 250); from the viewWillLayoutSubviews method the keyboard works, but the view is now a full sized UIModalPresentationFormSheet style.
This only happens in iOS 8, so I can only assume its an issue with the way the keyboard is presented and the way I am masking/resizing the view, but I'm at a loss as to the solution.
Presenting ViewController -
UserLoginViewController *loginVC = [[UserLoginViewController alloc] initWithNibName:#"UserLoginViewController" bundle:nil];
loginVC.modalPresentationStyle = UIModalPresentationFormSheet;
loginVC.delegate = self;
[self presentViewController:loginVC animated:YES completion:nil];
Editing the view bounds -
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.view.superview.layer.cornerRadius = 10.0;
self.view.superview.layer.masksToBounds = YES;
self.view.superview.bounds = CGRectMake(0, 0, 300, 250);
}
In iOS8 You shouldn't change superview bounds in viewWillLayoutSubviews because it causes infinite loop.
EDIT:
in iOS8 property preferredContentSize works well.
You should change your code in that way:
UserLoginViewController *loginVC = [[UserLoginViewController alloc] initWithNibName:#"UserLoginViewController" bundle:nil];
loginVC.modalPresentationStyle = UIModalPresentationFormSheet;
loginVC.delegate = self;
if(IS_IOS8)
{
loginVC.preferredContentSize = CGSizeMake(300, 250);
}
[self presentViewController:loginVC animated:YES completion:nil];
Editing the view bounds -
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
self.view.superview.layer.cornerRadius = 10.0;
self.view.superview.layer.masksToBounds = YES;
if(!IS_IOS8)
{
self.view.superview.bounds = CGRectMake(0, 0, 300, 250);
}
}
Another way, which gives you more customization options is to use UIPresentationController and UIViewControllerTransitioningDelegate. Take a look at my code below.
Parent view controller:
_aboutViewController = [[AboutViewController alloc] init];
_aboutViewController.modalPresentationStyle = UIModalPresentationFormSheet;
if(IS_IOS8)
{
if(aboutTransitioningDelegate == nil)
{
aboutTransitioningDelegate = [[AboutTransitioningDelegate alloc] init];
}
_aboutViewController.transitioningDelegate = aboutTransitioningDelegate;
_aboutViewController.modalPresentationStyle = UIModalPresentationCustom;
}
[self presentViewController:_aboutViewController animated:YES completion:nil];
AboutViewController.m
#import "AboutViewController.h"
#interface AboutViewController ()
#end
#implementation AboutViewController
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
if(IS_IOS8)
{
return;
}
CGSize displaySize = CGSizeMake(320, 462);
self.view.superview.bounds = CGRectMake(0, 0, displaySize.width, displaySize.height);
}
#end
AboutTransitioningDelegate.h:
#interface AboutTransitioningDelegate : NSObject <UIViewControllerTransitioningDelegate>
#end
AboutTransitioningDelegate.m:
#import "AboutTransitioningDelegate.h"
#import "AboutPresentationController.h"
#implementation AboutTransitioningDelegate
-(UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{
return [[AboutPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
}
#end
AboutPresentationController.h
#import <UIKit/UIKit.h>
#interface AboutPresentationController : UIPresentationController
#end
AboutPresentationController.m
#import "AboutPresentationController.h"
#implementation AboutPresentationController
-(CGRect)frameOfPresentedViewInContainerView
{
CGSize displaySize = CGSizeMake(320, 462);
if([[Config sharedInstance] latestVersionFromAppstoreInstalled])
{
displaySize = CGSizeMake(320, 416);
}
CGRect r = CGRectZero;
r.size = displaySize;
r.origin.y = self.containerView.bounds.size.height/2 - displaySize.height/2;
r.origin.x = self.containerView.bounds.size.width/2 - displaySize.width/2;
return r;
}
-(void)containerViewWillLayoutSubviews
{
[super containerViewWillLayoutSubviews];
self.presentedView.frame = [self frameOfPresentedViewInContainerView];
}
#end
ProjectName-Prefix.pch
#define IS_IOS8 ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8)
The simplest way to call containerViewWillLayoutSubviews() is to call:
containerView?.setNeedsLayout()
I have a UINavigationController to which I need to add a second UINavigationBar. Neither of those bars is translucent. Problem is, view controllers that I put inside this navigation controller are partially covered by my second navigation bar. Where do I adjust the frames of those view controllers' views so that I don't get a "blinking" effect of them changing frames while being visible?
EDIT:
This is in viewDidLoad:
UINavigationBar *secondaryNavBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 50)];
secondaryNavBar.translucent = NO;
if ([secondaryNavBar respondsToSelector:#selector(setBarTintColor:)]) { //it has to work on iOS 6 as well
secondaryNavBar.barTintColor = [UIColor darkGrayColor];
secondaryNavBar.tintColor = [UIColor whiteColor];
}
else {
secondaryNavBar.tintColor = [UIColor darkGrayColor];
}
[self.view addSubview:secondaryNavBar];
self.secondaryNavBar = secondaryNavBar;
Here's a working solution. Certainly not the best, and I did not make it to support iOS 6, you'll have to work on it and test it.
CustomNavigationController.m :
#implementation CustomNavigationController {
UINavigationBar *bottomNavBar;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self showNavBar];
}
- (void)showNavBar {
UINavigationBar *secondaryNavBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 50)];
secondaryNavBar.translucent = NO;
if ([secondaryNavBar respondsToSelector:#selector(setBarTintColor:)]) { //it has to work on iOS 6 as well
secondaryNavBar.barTintColor = [UIColor darkGrayColor];
secondaryNavBar.tintColor = [UIColor whiteColor];
}
else {
secondaryNavBar.tintColor = [UIColor darkGrayColor];
}
[self.view addSubview:secondaryNavBar];
bottomNavBar = secondaryNavBar;
[self layoutNavBar];
}
- (void)layoutNavBar {
// Get the currently displayed view
UIView *contentView = self.topViewController.view;
// Get its frame and height
CGRect contentFrame = contentView.frame;
float height = contentFrame.size.height;
// Adapt height and y origin with the new nav bar
contentFrame.size.height = height - bottomNavBar.frame.size.height;
contentFrame.origin.y = bottomNavBar.frame.origin.y + bottomNavBar.frame.size.height;
// Set the view's frame
contentView.frame = contentFrame;
}
#end
ViewController.m :
#implementation ViewController
-(void)viewDidAppear:(BOOL)animated {
CustomNavigationController *navigation = (CustomNavigationController*)self.navigationController;
[navigation layoutNavBar];
}
#end
Note that you have to call layoutNavBar on viewDidAppear, or the view's frame will be reset by your app. This is not a perfectly clean solution, but a pretty good fix.
I have a requirement to show a status bar at certain times at the bottom of my application. I can easily put this at the bottom of my application's main view, but whenever I push a view controller on top of this (either modally or not) it hides this status bar.
Is there any way I can add a status bar like this, and have it be outside the bounds of my application itself? Ideally I'd like this to work like the call-in-progress status bar on the iPhone - when this bar appears, the app is pushed down, and a call to [[UIScreen mainScreen] applicationFrame] returns the correct size (i.e. it accounts for the presence of this status bar when calculating the height available for the app).
I wanted to do this, too, so I tried View Controller Containment. I'm still trying it out, so I'm not willing to give this a ringing endorsement, but it might be something you'd want to try playing around with yourself if you're in iOS5. But it appears to give you a status bar that will appear or disappear from the bottom of the screen.
This is a view controller that will open another view controller, but if there is status text to show, it pops up from the bottom of the screen and stays there until you get rid of it. I've only done a little testing so far, but it looks like this handles pushViewController/popViewController, but maybe not modal views.
My header looks like:
// StatusBarViewController.h
//
// Created by Robert Ryan on 7/8/12.
#import <UIKit/UIKit.h>
#interface StatusBarViewController : UIViewController
#property (strong, nonatomic) UIViewController *appController;
- (void)setStatus:(NSString *)text;
#end
My implementation file (this is ARC) looks like:
// StatusBarViewController.m
//
// Created by Robert Ryan on 7/8/12.
#import "StatusBarViewController.h"
#interface StatusBarViewController ()
{
BOOL _statusHidden;
UIView *_appView;
UILabel *_statusLabel;
}
#end
#implementation StatusBarViewController
#synthesize appController = _appController;
- (void)dealloc
{
_appView = nil;
_statusLabel = nil;
[self setAppController:nil]; // usually I don't like setters in dealloc, but this does some special stuff
}
- (void)createControlsWithStatusHidden
{
// create default app view that takes up whole screen
CGRect frame = self.view.frame;
frame.origin = CGPointMake(0.0, 0.0);
_appView = [[UIView alloc] initWithFrame:frame];
_appView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_appView.clipsToBounds = YES;
[self.view addSubview:_appView];
// create status label that is just off screen below the app view
_statusLabel = [[UILabel alloc] init];
_statusLabel.font = [UIFont fontWithName:#"Helvetica-Bold" size:12.0];
_statusLabel.backgroundColor = [UIColor darkGrayColor];
_statusLabel.textColor = [UIColor whiteColor];
CGSize size = [#"Hey!" sizeWithFont:_statusLabel.font]; // test size of box with random text
_statusLabel.frame = CGRectMake(0.0, frame.size.height, frame.size.width, size.height);
_statusLabel.textAlignment = UITextAlignmentCenter;
_statusLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:_statusLabel];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self createControlsWithStatusHidden];
_statusHidden = YES;
// I'm instantiating from storyboard. If you're using NIBs, just create your controller controller using initWithNib and then set our appController accordingly.
self.appController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainNavigator"];
}
- (void)setAppController:(UIViewController *)controller
{
if (controller)
{
controller.view.frame = CGRectMake(0.0, 0.0, _appView.frame.size.width, _appView.frame.size.height);
[self addChildViewController:controller];
[controller didMoveToParentViewController:self];
if (self.appController)
{
// if we have both a new controller and and old one, then let's transition, cleaning up the old one upon completion
[self transitionFromViewController:self.appController
toViewController:controller
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseInOut
animations:nil
completion:^(BOOL finished){
if (self.appController)
{
[self.appController willMoveToParentViewController:nil];
[self.appController removeFromParentViewController];
}
}];
}
else
{
// if we have no previous controller (i.e. this is our first rodeo), then just add it to the view
[_appView addSubview:controller.view];
}
}
else
{
// no new controller, so we're just removing any old on if it was there
if (self.appController)
{
// if there was an old controller, remove it's view, and remove it from the view controller hierarchy
[self.appController.view removeFromSuperview];
[self.appController willMoveToParentViewController:nil];
[self.appController removeFromParentViewController];
}
}
_appController = controller;
}
- (void)hideStatusWithCompletion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:0.25
animations:^{
CGRect labelFrame = _statusLabel.frame;
labelFrame.origin.y += labelFrame.size.height;
_statusLabel.frame = labelFrame;
CGRect appFrame = _appView.frame;
appFrame.size.height += labelFrame.size.height;
_appView.frame = appFrame;
}
completion:completion];
}
- (void)unhideStatusWithCompletion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:0.25
animations:^{
CGRect labelFrame = _statusLabel.frame;
labelFrame.origin.y -= labelFrame.size.height;
_statusLabel.frame = labelFrame;
CGRect appFrame = _appView.frame;
appFrame.size.height -= labelFrame.size.height;
_appView.frame = appFrame;
}
completion:completion];
}
- (void)setStatus:(NSString *)text
{
BOOL hasText = (text && [text length] > 0);
if (hasText)
{
if (!_statusHidden)
{
// if we have text, but status is already shown, then hide it and unhide it with new value
[self hideStatusWithCompletion:^(BOOL finished){
_statusLabel.text = text;
[self unhideStatusWithCompletion:nil];
}];
}
else
{
// if we have text, but no status is currently shown, then just unhide it
_statusLabel.text = text;
[self unhideStatusWithCompletion:nil];
}
_statusHidden = NO;
}
else
{
if (!_statusHidden)
{
// if we don't have text, but status bar is shown, then just hide it
[self hideStatusWithCompletion:^(BOOL finished){
_statusLabel.text = text;
}];
_statusHidden = YES;
}
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
And then, any view controller that wants to update the status message would use a method kind of like:
- (void)setStatus:(NSString *)text
{
UIViewController *controller = [UIApplication sharedApplication].delegate.window.rootViewController;
if ([controller isKindOfClass:[StatusBarViewController class]])
{
[(StatusBarViewController *)controller setStatus:text];
}
}