I have a UIViewController that I'm using to control a "pop-up" view for viewing images throughout my application. It supports autorotation, as it automatically sizes the image to fit properly regardless of orientation. This works perfectly, but only the first time I initialize and display the view controller. When it closes, I am removing the UIView from my view hierarchy and releasing the view controller - but the next time I instantiate and add it to my view hierarchy, it stops receiving the -shouldAutorotateToInterfaceOrientation messages when the phone is rotated.
This is how I instantiate and display it:
popupVC = [[PopupVC alloc] init];
[popupVC viewWillAppear:NO];
[[[UIApplication sharedApplication] keyWindow] addSubview:popupVC.view];
[popupVC viewDidAppear:NO];
this is how I remove/release it when it's finished:
[popupVC viewWillDisappear:NO];
[popupVC.view removeFromSuperview];
[popupVC viewDidDisappear:NO];
[popupVC release];
popupVC = nil;
I've tried looping through [[UIApplication sharedApplication] keyWindow] subviews to see if somehow my popup view isn't on top, but it always is. And it has a different address each time so I do know that it's a different instance of the view controller class.
As requested, here is the complete loadView method from PopupVC:
- (void)loadView {
UIView *myView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
myView.backgroundColor = self.overlayColor;
myView.autoresizesSubviews = NO;
myView.hidden = YES;
myView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.view = myView;
[myView release];
_isVisible = NO;
UIView *myMaskView = [[UIView alloc] initWithFrame:self.view.bounds];
myMaskView.backgroundColor = [UIColor clearColor];
myMaskView.clipsToBounds = YES;
myMaskView.hidden = YES;
myMaskView.autoresizesSubviews = NO;
myMaskView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:myMaskView];
self.imageMaskView = myMaskView;
[myMaskView release];
UIImageView *myImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
myImageView.center = self.view.center;
myImageView.hidden = NO;
myImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleHeight;
[self.imageMaskView addSubview:myImageView];
self.imageView = myImageView;
[myImageView release];
UIButton *myImageButton = [UIButton buttonWithType:UIButtonTypeCustom];
myImageButton.frame = self.view.frame;
myImageButton.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleHeight;
[myImageButton addTarget:self action:#selector(clickImage:) forControlEvents:UIControlEventTouchUpInside];
[self.imageMaskView addSubview:myImageButton];
self.imageButton = myImageButton;
UIActivityIndicatorView *myActivityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
myActivityView.hidden = YES;
[self.view addSubview:myActivityView];
myActivityView.center = self.view.center;
self.activityView = myActivityView;
[myActivityView release];
}
The shouldAutorotateToInterfaceOrientation handler isn't the place for custom code to respond to rotation events. It's purpose is just to tell the OS that your view controller can rotate. If you want to have custom code to handle the rotation events you should overide - didRotateFromInterfaceOrientation or one of the other similar callback methods depending on your needs:
willRotateToInterfaceOrientation:duration:
willAnimateRotationToInterfaceOrientation:duration:
didRotateFromInterfaceOrientation:
willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:
didAnimateFirstHalfOfRotationToInterfaceOrientation:
willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:
See Autorotating Views in the developer docs.
I think that might be a bug in the new OS 3.0. A workaround to this would be to use NSNotificationCenter after turning on beginGeneratingDeviceOrientationNotifications.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(receivedRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
-(void) receivedRotate: (NSNotification *) notification {
DebugLog(#"ORIENTATION CHANGE");
UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if(interfaceOrientation == UIDeviceOrientationPortrait) {
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(0));
self.view.bounds = CGRectMake(0, 0, 320, 480);
}
else if(interfaceOrientation == UIDeviceOrientationLandscapeLeft) {
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
self.view.bounds = CGRectMake(0, 0, 480, 320);
}
else if(interfaceOrientation == UIDeviceOrientationLandscapeRight) {
self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(-90));
self.view.bounds = CGRectMake(0, 0, 480, 320);
}
}
My problem is that I get the rotation event for my rootViewController, but as soon as I launch my second viewController, it won't get any signals except motion (shaking) and touch. Is that the same issue as the original post -- it is certainly similiar??
I followed-up with above recommendation and I don't have the selector coded correctly so it throws an exception as soon as I try to rotate. Meanwhile, I found this other discussion on the web which confirms that the problem was introduced in 3.0.
link text
Related
I am developing one app which is based on tabbarcontroller.In one view Controller i want to hidden the tabbarController instead of that i want to show toolbar for that purpose i wrote the following code
//tabBAr hidden
[self.tabBarController.tabBar setHidden:YES];
//creation of tool bar
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
tb = [[UIToolbar alloc] init];
//[tb setBackgroundImage:[UIImage imageNamed:#"tabbar.png"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
tb.tintColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
if ([[UIDevice currentDevice]userInterfaceIdiom]==UIUserInterfaceIdiomPhone)
{
tb.frame = CGRectMake(0, delegate.window.frame.size.height-50, 320, 44);
}
else
{
tb.frame = CGRectMake(0, delegate.window.frame.size.height-70, 768, 44);
}
[delegate.window addSubview:tb];
But problem is in iPad i want to change orientation of toolbar but it does not change it always takes portrait width and height.
Use notification to detect the device orientation like the following.
// Start Orientation //
- (void)orientationChanged:(NSNotification *)notification{
[self adjustViewsForOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
{
//NSLog(#"Vertical");
}
else if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
//NSLog(#"Horizontal");
}
}
// End Orientation //
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
Somebody has kindly written this notification code somewhere. I didn't write it.
use viewWillLayoutSubViews method and add code to setFrame of toolbar in that method
ex:
- (void) viewWillLayoutSubviews
{
[toolBar setFrmae:CGRectMake(self.view.bounds.origin.x, self.view.bounds.size.height - 44, self.view.bounds.size.width, 44)];
}
I want to add a Logo (UIImageView) to a fullscreen MPMoviePlayerController.
It works fine when the MPMoviePlayerController is not in fullscreen Mode. When i switch to full screen all added views on the MPMoviePlayerController removed(hidden).
Here is my code:
- (void)setupMPMoviePlayer{
[self.moviePlayer.view removeFromSuperview];
[self removeNotifications];
self.moviePlayer = nil;
self.moviePlayer = [[MPMoviePlayerController alloc] init];
[self addNotifications];
[self.moviePlayer.view setFrame:self.view.bounds];
self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.moviePlayer.fullscreen = NO;
[self.view addSubview:self.moviePlayer.view];
if ([self.moviePlayer respondsToSelector:#selector(setAllowsAirPlay:)])
[self.moviePlayer setAllowsAirPlay:YES];
[self.moviePlayer setContentURL:[NSURL URLWithString:urlString]];
[self.moviePlayer setFullscreen:YES animated:YES];
[self addLogoViewAtView:self.moviePlayer.view];
}
- (void)addLogoViewAtView:(UIView *)view{
UIView *theView = [view viewWithTag:101];
if (theView.superview) {
[theView removeFromSuperview];
}
UIImageView *logoView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"logo_weiss.png"]];
logoView.tag = 101;
logoView.frame = CGRectMake( logoView.image.size.width - 100.0,
100.0,
logoView.image.size.width,
logoView.image.size.height);
logoView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin;
[view addSubview:logoView];
[view bringSubviewToFront:logoView];
}
Now my question is there an alternate way to add an subview to MPMoviePlayerController?
I can add the logo to the application's window but this ist not so clean.
Anyone have any idea on how I could do this?
Most propably fullscreen mode add MPMoviePlayerController view at keyWindow for an application or present a modal view controller above. So, you can try looking for the view in window.subviews or modal view controllers for moviePlayer.
I'm doing a bit of AR work here and one of the biggest problem right now is one of my UIView will not respond to any frame settings I make to it.
The code is very similar to this. I've made a few major changes to incorporate into my project:
nurse.site11.com/JaredCrawford-iWVU-632a9a0/Libraries/ARKit/AugmentedRealityController.m
"displayView" is used as a custom UIView that is passed to UIImagePickerController's setCameraOverlayView: method.
Now in the initWithViewController method of that AugmentedRealityController.m file, I have tried setting the displayView = [[UIView alloc] initWithFrame:CGRectMakeFrame(0, 0, 480, 320)];
The problem is, even if I set it to displayView = [[UIView alloc] initWithFrame:CGRectMakeFrame(0, 0, 100, 100)]; the view still remains full screen 320 x 480 BUT only for the first time the Augmented Reality view appeared. Subsequent times that the AR view appears, the code works.
I am not sure what the heck is going on and why setting the frame of the displayView not affecting the view at all FOR THE FIRST TIME it is shown.
I'm hoping someone else has experienced this problem before and possibly solved it.
This is my code:
- (id)initWithViewController:(UIViewController *)vc
{
coordinates = [[NSMutableArray alloc] init];
coordinateViews = [[NSMutableArray alloc] init];
latestHeading = -1.0f;
debugView = nil;
[self setRootViewController: vc];
[self setDebugMode:NO];
[self setMaximumScaleDistance: 0.0];
[self setMinimumScaleFactor: 1.0];
[self setScaleViewsBasedOnDistance: YES];
[self setRotateViewsBasedOnPerspective: YES];
[self setMaximumRotationAngle: M_PI / 6.0];
//CGRect screenRect = [[UIScreen mainScreen] bounds];
NSLog(#"inside initWithViewController");
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationPortrait)
{
NSLog(#"Orientation portrait");
}
else if(orientation == UIDeviceOrientationLandscapeRight)
{
NSLog(#"orientation right");
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft];
[self setCurrentOrientation:UIDeviceOrientationLandscapeRight];
}
else if(orientation == UIDeviceOrientationPortraitUpsideDown)
{
NSLog(#"orientation upsidedown");
}
else if(orientation == UIDeviceOrientationLandscapeLeft){
NSLog(#"orientation left");
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
[self setCurrentOrientation:UIDeviceOrientationLandscapeLeft];
}
//[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft];
//NSLog(#"In AR view, device orientation = %#", [[UIDevice currentDevice] orientation]);
//[self setDisplayView: [[UIView alloc] initWithFrame: screenRect]];
//[self setCurrentOrientation:UIDeviceOrientationLandscapeRight];
displayView = [[UIView alloc] initWithFrame:CGRectMake(-80, 80, 480, 320)];
[displayView setBackgroundColor:[UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:0.5]];
NSLog(#"displayView x = %f", displayView.frame.origin.x);
NSLog(#"displayView y = %f", displayView.frame.origin.y);
NSLog(#"displayView width = %f", displayView.frame.size.width);
NSLog(#"displayView height = %f", displayView.frame.size.height);
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deviceOrientationDidChange:) name: UIDeviceOrientationDidChangeNotification object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[self rootViewController] setView:displayView];
//[[self rootViewController] setView:[[UIView alloc] initWithFrame:CGRectMake(0, 0, 480, 320)]];
//[[rootViewController view] setBackgroundColor:[UIColor blueColor]];
CLLocation *newCenter = [[CLLocation alloc] initWithLatitude:37.41711 longitude:-122.02528];
[self setCenterLocation: newCenter];
[newCenter release];
[self startListening];
[self deviceOrientationUpdate];
startLocation = nil;
i = 0;
objImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,480)];
//[objImageView setBackgroundColor:[UIColor blueColor]];
blnHasRotated = NO;
return self;
}
The full code can be seen here: pastebin.com/g0eXJaPq
I am loading a view as soon as the user rotates to landscape but when I use view.bounds in viewDidLoad it is still in portrait.
Here the code where I load the view which is triggered from a notification
- (void)orientationChanged:(NSNotification *)notification
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController:self.graphViewController animated:YES];
isShowingLandscapeView = YES;
} else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
}
And in the graphViewController in viewDidLoad I use self.view.bounds to initialise a core-plot.
This is how it looks like
Any hint on how to fix that?
Thanks a lot
EDIT
in viewDidLoad
// Create graph from theme
graph = [[CPTXYGraph alloc] initWithFrame:self.view.bounds];
CPTTheme *theme = [CPTTheme themeNamed:kCPTDarkGradientTheme];
[graph applyTheme:theme];
CPTGraphHostingView *hostingView = [[CPTGraphHostingView alloc] initWithFrame:self.view.bounds];
hostingView.collapsesLayers = NO; // Setting to YES reduces GPU memory usage, but can slow drawing/scrolling
hostingView.hostedGraph = graph;
[self.view addSubview:hostingView];
Assuming CPTGraphHostingView will adjust the orientation of how it renders, you need to set the autoresizingMask on it. This determines how views resize when their superviews resize, which is happening in this case.
hostingView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
It's not the correct approach. You should be doing this stuff in layoutSubviews. It will be called first time in, and subsequently each time orientation changes. Do something like this:
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect frame = self.bounds;
// Do your subviews layout here based upon frame
}
I'm trying to do the following:
alt text http://img101.imageshack.us/img101/7396/33959221.png
I need to do a lot of custom things, so I subclassed everything.
alt text http://img248.imageshack.us/img248/4201/49422483.png
I'm having issues with autoresize and positioning. For instance the UIToolBar is not at the correct position.
So here is the question:
If you don't use UITableViewController or any other Controller, that takes care of all the positioning and sizing, then you have to do it yourself.
Can someone someone tell me how this works? What do I need to understand?
---- UPDATE:
My custom goals are actually not that important as my question is actually about something else. But OK. So all "elements" like the TableView, the Toolbar and the TabBar will have custom backgrounds and use custom buttons. The TableView will be GroupedStyle and the groups can be collapsed.
My question is how do I have to structure my code especially regarding positioning and autoresizing in order to achieve what you can see in picture #1.
This is how it's structured right now:
Application Delegate:
- (void) loadView {
RootViewController *rootView = [[RootViewController alloc] init];
[window addSubview:rootView.view];
[window makeKeyAndVisible];
RootViewController:
- (void) loadView {
self.theView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.theView.autoresizesSubviews = YES;
self.theView.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
self.view = self.theView;
ChildViewController *childView = [[ChildViewController alloc] init];
[self.view addSubview:childView.view];
ChildViewController:
- (void) loadView {
self.theView = [[UIView alloc] init];
self.view = self.theView;
}
- (void) viewDidLoad {
self.view.frame = [[UIScreen mainScreen] bounds];
CGRect toolbarBounds = self.theToolbar.bounds;
CGRect viewBounds = self.view.bounds;
CGRect tableViewBounds = CGRectMake(0, CGRectGetHeight(toolbarBounds), CGRectGetWidth(viewBounds), CGRectGetHeight(viewBounds) - CGRectGetHeight(toolbarBounds));
self.view.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth);
self.theTableView = [[MyTableView alloc] initWithFrame:tableViewBounds
style:UITableViewStyleGrouped
parent:self];
[self.view addSubview:self.theToolbar];
[self.view addSubview:self.theTableView];
}
MyToolBar:
- (id)initWithParent:(id)theParent {
if ((self = [self init])) {
self.parent = theParent;
CGRect rectArea = CGRectMake(0, 0, CGRectGetWidth([[[self parent] view] bounds]), 44);
[self setFrame:rectArea];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[self loadButtons];
}
return self;
}
So this is how the positioning is structured at the moment (messy). It actually works, BUT I have a ModalView which is handled by the ChildViewController. When I have the iPad in Portrait view and I pop up the modal view and dismiss it, then everything works. But when I do the same on Landscape view it rotates the hole view... the ChildViewController by 90 degrees when dismissing the modal view. Very strange. When I load directly ChildViewController in my AppDelegate, then again everything works fine.
You're missing the UIViewAutoresizingFlexibleBottomMargin option in the autoresize mask of your toolbar, for instance.