I created an UIViewController subclass, and figured out that the default implementation of -loadView in UIViewController will ignore my frame size settings in a strange way.
To simplify it and to make sure it's really not the fault of my code, I did a clean test with a plain instance of UIViewController directly, rather than making a subclass. The result is the same. I try to make an exactly quadratic view of 320 x 320, but the view appears like 320 x 200.
iPhone OS 3.0, please check this out:
UIViewController *ts = [[UIViewController alloc] initWithNibName:nil bundle:nil];
ts.view.frame = CGRectMake(0.0f, 0.0f, 320.0f, 320.0f);
ts.view.backgroundColor = [UIColor cyanColor];
[self.view addSubview:ts.view];
like you can see, I do this:
1) Create a UIViewController instance
2) Set the frame of the view to a quadratic dimension of 320 x 320
3) Give it a color, so I can see it
4) Added it as a subview.
Now the part, that's even more strange: When I make my own implementation of -loadView, i.e. if I put this code in there like this:
- (void)loadView {
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 320.0f)];
v.backgroundColor = [UIColor cyanColor];
self.view = v;
[v release];
}
then it looks right.
Now lets think about that: In the first example, I do pretty much exactly the same, just that I let UIViewController create the view on it's own, and then take it over in order to change it's frame. Right?
So why do I get this strange error? Right now I see no other way of messing around like that to correct this wrong behavior. I did not activate anything like clipsToBounds and there's no other code touching this.
The dimensions of the view of a view controller should not be changed. It should be autoresized to fit the size of the window or the parent controller.
If you really need a square view, make a subview.
// Note: better do this in -loadView or -viewDidLoad.
UIView* container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
[ts.view addSubview:container];
[container release];
// add stuff into the container view.
// ...
Related
I have a viewController inside of a navigationController, the view controller has a tableview.
In viewDidLoad I set the tableview
- (void)viewDidLoad
{
[super viewDidLoad];
// init tableView
CGRect tableFrame = self.view.bounds;
_tableView = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
[self.view addSubview:_tableView];
}
The problem with this code is that the table view frame is not correct - the height is 460 and I need it to be 416.
[The iPhone screen height is 480, minus the status bar (20) minus the navigation bar (44) = 416]
So what is the proper way to set the table view so it will fill the screen?
I can think of two ways:
set its frame to = (0, 0, 320, 416)
use: [_tableView setAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth)];
Don't use magic numbers. Use the resizing flags correctly.
So yes, your 2. approach is correct.
1) Use the superviews bounds._tableView.frame = self.view.bounds;;
2) Set autoresizing flags [_tableView setAutoresizingMask: UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
(you did all of this already :)
Number 1 is absolutely the wrong way to do it... what happens if the screen size changes in a future OS / device?
I'm curious why you're not doing this using a nib file, and saving yourself the trouble, but if you must do it in code, set the auto-resizing mask per your option 2.
I have two UIViewControllers each with their own nib files. Each controller serves a different purpose. The idea was to add a UIScrollController and add both UIViewControllers to it so the user can easily scroll between them both.
Q1) Is this even possible? Or have I got the whole purpose of UIScollControllers incorrect, if so, what is the best alternative.
I have been poking around the internet and the best I can come up with is this:
ScoreViewController *scoreController = [[ScoreViewController alloc] initWithNibName:#"ScoreViewController" bundle:nil];
scoreController.view.frame = CGRectMake(0.0f, 0.0f, 960f, 640f);
[self.scrollview addSubview:scoreController.view];
[scoreController release];
SettingsViewController *settingsController = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
settingsController.view.frame = CGRectMake(0.0f, 0.0f, 960f, 640f);
[self.scrollview addSubview:settingsController.view];
[settingsController release];
This code is entered inside my RootViewController class. It doesn't seem to work though as I can't scroll between the two UIViewControllers I have added (score and settings). All what is displayed on the screen is the second controller added - the SettingsViewController.
Q2) Why is this broken?
Thank you. :)
Actually you place the first controller correctly, then you put the second above it.
settingsController.view.frame = CGRectMake(0.0f, 0.0f, 960f, 640f);
960x640 - this is not the correct way to enter coordinates (320x480 is the actual size) double size is used only for image files
0.0f, 0.0f means x: 0, y: 0, so in the second controller you need x: 320, y: 0
then, you have to set the contentSize of the scrollView
scrollview.contentSize = CGSizeMake(320*2, 480);
Then enable the paging of the scrollView;
scrollview.pagingEnabled = YES;
And that should work :)
I have made a custom UIView which is shown when the user hits a button in the navigationbar. I make my view's in code. In my loadview I set the autoresizing masks and the view loads correct on screen. However the UIView which is shown when the user taps the button does not resize even when I have set the autoresizing masks.
UIView *blackView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 416.0)];
blackView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Do I need to use self.view.frame.size.width and self.view.frame.size.height instead? And if I do why? Does not resizing masks work outside of loadView?
Thank you for your time:)
the autoresizingMask affects how a view will behave when its superviews frame changes. if all you are doing is showing theblackViewwhen you tap a button, thenblackView` will have whatever frame you initially set for it.
If this isn't enough info, please post some more code around how you are configuring and displaying blackView and it's superview and explain more about what situations you are expecting blackView to resize in. Rotation is one of them, if that's what you're concerned with.
First things first, I hope you've done this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Let's say the view that needs resizing is: view2
The view that has view2 as a subview is: view1
While creating view1 you would declare it as:
view1 = [[UIView alloc] init];
[view1 setNeedsLayout];
Now in view1's .m file you need to overload the layoutSubviews method as shown:
- (void)layoutSubviews
{
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
In case view1 is a view controller's view, you need to do that same thing as above in the willRotate method as shown
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
This is a tried and tested method that I use to handle orientation changes.
I have a root UIViewController that I am adding other UIViewController's as subviews. Currently each of the subviews are too low down (covering up my custom build tabbar). When I try to so something like the following, it does not work:
// Test setting frame size to see if it works
self.view.frame = CGRectMake(0, 0, 200, 200);
That does nothing to change the frame size.
So, my question is, how can I set my frame when the UIViewController is initialized after it is added as the subview?
#Nic i think when you are adding that other view, at that time you should define the other views frame size like this:
Someviewcontroller *c = initWithNibName
c.view.frame = CGRectMake(0, 0, 200, 200);
[self addSubView:c];
i dont know if this will work but it is something like this.
First, ensure that your frame is actually not changing size. Likely it /is/ changing size, but you are expecting it to clip its contents; this behavior is not enabled by default on a UIView, and would need to be set via:
[[self view] setClipsToBounds:YES];
To double check and ensure that your frame is / is not changing size after setting the new frame, try logging this:
NSLog(#"New frame is: %#", NSStringFromCGRect([[self view] frame]));
or simply setting a breakpoint after the change and inspecting the value in your debugger.
Then there's always...
// Screen, less StatusBar
CGRect l_RectFrame = [UIScreen mainScreen].applicationFrame;
OR...
// Entire Screen
CGRect l_RectFrame = [UIScreen mainScreen].bounds;
MyView *l_aView = [[MyView alloc] initWithFrame:l_RectFrame];
...
I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}