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
}
Related
For a exercise I created a project to experiment with the accelerometer functions of the iPhone. Right now when I run the app on my device it begins with a blank screen, shake the phone and a image is displayed.
I have to force close the app to clear the image. I was hoping someone could provide a solution that would make the image reset so I could repeat the process as many times as I wanted. (shake phone, display image, clear image) I'm thinking it needs a timer or something, not sure. Here is the source code. Thanks for taking the time to read and help.
// ViewController.m
// AccelTest
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
-(void)viewWillAppear:(BOOL)animated{
[self startAccel];
[self view];
}
-(void)viewWillDisappear:(BOOL)animated{
[self stopAccel];
[self view];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return NO;
}
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
double const kThreshold = 2.0;
// double const kThreshold = 2.0;
if ( fabsf(acceleration.x) > kThreshold
|| fabsf(acceleration.y) > kThreshold
|| fabsf(acceleration.z) > kThreshold){
[self.view addSubview:[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Icon.png"]]];
}
}
-(void)startAccel{
UIAccelerometer * accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = self;
accel.updateInterval = .25;
}
-(void)stopAccel{
UIAccelerometer * accel = [UIAccelerometer sharedAccelerometer];
accel.delegate = nil;
}
#end
Here is how I would do it (without ARC) to tap the image to make it disappear.
Remove your line:
[self.view addSubview:[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"Icon.png"]]];
And add these lines instead:
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Icon.png"]];
myImageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(rm:)];
[myImageView addGestureRecognizer:tapgr];
[tapgr release]; tapgr = nil;
[self.view addSubview:myImageView];
[myImageView release]; myImageView = nil;
Then add a method to the View Controller to remove the UIImageView when it is tapped.
-(void)rm:(UITapGestureRecognizer *)tapgr {
[tapgr.view removeFromSuperview];
}
When that Image is tapped once, the rm: method will be called which will remove the Image from self.view
Store a pointer to that image view somewhere and remove it from it's superview when you want to. Either with a timer, or user action, or something like that.
my problem might be simple to some of you but i can't find a solution. I have a method where a create my views automatically (text fields, labels and text views). It's like a registration form. The problem is that when i change the screen to landscape mode, i want to change the views width. To do that i used [self.view removeFromSuperview] and created the views again. The problem is that the views won't get recreated with the landscape width. I can't use IB to autosize the views at orientation change. The views are created in viewDidLoad and removed and recreated in - (void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation {} I don't know why they don't get recreated after being removed. If there was another solution that you could share with me i would appreciate it.
Here's how i create the views:
-(void)createViews:(int)width
{
for(int i = 0; i < numberOfTextfields; i++) {
textfieldPadding = textfieldPadding+40;//set some space between the text fields
labelPadding = labelPadding+40;//set some space between the labels
UITextField *field = [[UITextField alloc] initWithFrame:
CGRectMake(10,i*textfieldHeight+textfieldPadding+firstTextfieldHeight+10,width, textfieldHeight)];
field.autoresizingMask = UIViewAutoresizingFlexibleWidth;
//field.backgroundColor= [UIColor cyanColor];
field.placeholder = [labels objectAtIndex:i];
field.borderStyle=UITextBorderStyleRoundedRect;
field.returnKeyType = UIReturnKeyDone;
[field addTarget:self action:#selector(doneButton:)
forControlEvents:UIControlEventEditingDidEndOnExit];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, i*labelHeight+firstLabelHeight+labelPadding-20, width, labelHeight)];
label.text = [labels objectAtIndex:i];
//label.backgroundColor = [UIColor brownColor];
[scrollView addSubview:field];
[scrollView addSubview:label];
[textfields addObject:field];
[labels addObject:label];
[field release];
[label release];
}
}
Here's where i wanted to remove them and recreate them:
- (void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation
{
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)
{
[self.view removeFromSuperview];
[self createViews:landscapeWidth];
}
else if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)
{
[self.view removeFromSuperview];
[self createViews:portraitWidth];
}
}
Thanks in advance!
Instead of removing, resizing and then re-adding as you currently are why not just set the appropriate autoresizingMask (link) when you create the view initially?
UITextField *myTextField = [[UITextField alloc] initWithFrame:someRect];
// the following will automatically resize the width on orientation change
myTextField.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[myView addSubview:myTextField];
[myTextField release];
I have a viewcontroller that is set to scrollview delegate (the scrollview is a subview of the viewcontroller), when I put [scrollview removeFromSuperview]; both the viewcontroller and the scrollview get removed and I'm just left with an empty window.
How can I remove only the scrollview?
edit::::
this is part of my viewcontroller .h
#interface QuartzViewController : UIViewController <UIScrollViewDelegate> {
this is part of my viewcontroller .m
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
UIInterfaceOrientation o = self.interfaceOrientation;
if ((o == UIInterfaceOrientationPortrait) || (o == UIInterfaceOrientationPortraitUpsideDown)) {D = 1;pagingScrollView.delegate = nil;[pagingScrollView removeFromSuperview];[self resurrectPaging];}
if ((o == UIInterfaceOrientationLandscapeLeft) || (o == UIInterfaceOrientationLandscapeRight)) {D = 2;pagingScrollView.delegate = nil;[pagingScrollView removeFromSuperview];[self resurrectPaging];}}
-(void)resurrectPaging {
CGRect F;
F = [self frameForPagingScrollView];
pagingScrollView = [[UIScrollView alloc] initWithFrame:F];
[SELF addSubview:pagingScrollView];
[self setPaging];}
- (void)viewDidLoad {
[self.view addSubview:SELF];
D = 1;
CGRect F;
F = [self frameForPagingScrollView];
pagingScrollView = [[UIScrollView alloc] initWithFrame:F];
[self setPaging];}
- (void)setPaging {
if (D == 1) {
CGRect F;
F = [self frameForPagingScrollView];
pagingScrollView.pagingEnabled = YES;
pagingScrollView.backgroundColor = [UIColor whiteColor];
pagingScrollView.showsVerticalScrollIndicator = NO;
pagingScrollView.showsHorizontalScrollIndicator = NO;
pagingScrollView.contentSize = CGSizeMake(F.size.width * [self pdfPageCount], F.size.height);
pagingScrollView.delegate = self;
pagingScrollView.clipsToBounds = YES;
pagingScrollView.bounces = YES;
self.view = pagingScrollView;
recycledPages = [[NSMutableSet alloc] init];
visiblePages = [[NSMutableSet alloc] init];
[self tilePages];
[pagingScrollView addSubview:self.isvP];}
else if (D == 2) {
CGRect F;
F = [self frameForPagingScrollView];
pagingScrollView.pagingEnabled = YES;
pagingScrollView.backgroundColor = [UIColor redColor];
pagingScrollView.showsVerticalScrollIndicator = NO;
pagingScrollView.showsHorizontalScrollIndicator = NO;
pagingScrollView.contentSize = CGSizeMake(F.size.width * [self pdfPageCount], F.size.height);
pagingScrollView.delegate = self;
pagingScrollView.clipsToBounds = YES;
pagingScrollView.bounces = YES;
self.view = pagingScrollView;
recycledPages = [[NSMutableSet alloc] init];
visiblePages = [[NSMutableSet alloc] init];
[self tilePages];
[pagingScrollView addSubview:self.isvL];}}
basically the reason i what to remove then add my scrollview again is because when the device orientation changes to landscape my landscape subview is in the wrong place (x, y).
but say if i start off in landscape isvL is in the right place so I know I got the code correct. going to landscape screws the x, y positions of isvL in my pagingScrollView!
basically the reason i what to remove then add my scrollview again is because when the device orientation changes to landscape my landscape subview is in the wrong place (x, y).
If you having trouble with the positioning of your scroll view, you should try setting autoresize masks correctly, or manually adjusting the frame. There is no reason to remove and recreate a scroll view every time.
Also, in the code you have listed, you appear to be leaking your scroll view.
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.
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