I can't figure out why that view takes the entire screen.
In AppDelegate file
...
self.viewController = [[[ViewController alloc]init]autorelease];
[self.window setRootViewController:self.viewController];
self.window.backgroundColor = [UIColor whiteColor];
..
In ViewController.m
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(30, 30, 30, 30)];
[view setBackgroundColor:[UIColor greenColor]];
self.view = view;
When I run the app the screen is entirely green instead of having just a square in green.
What is wrong here ?
The erroneous line is here:
self.view = view;
When you set a view of a UIViewController that is the root controller, it is guaranteed to fill the screen. Instead, add it as a subview:
[self.view addSubview:view];
And you should be fine.
The view controller automatically manages the size of its root view (self.view), so even if you initialize it with a smaller size it will later get resized to fill the screen. This resizing conveniently also happens when the interface orientation changes (see the answer this question).
As suggested by Richard's answer, you can add your green view as a subview to the controller's root view. The crash you get is probably because the root view does not exist yet when you try to access it. Try the following:
- (void) loadView
{
[super loadView]; // creates the root view
UIView* subView = [[UIView alloc] initWithFrame:CGRectMake(30, 30, 30, 30)];
[subView setBackgroundColor:[UIColor greenColor]];
// because you don't set any autoresizingMask, subView will stay the same size
[self.view addSubview:subView];
}
Related
Since the template of an OS X app in Xcode seems to be similar to an empty app template, the following is used to add a view and a button (trying not to use Interface builder for now):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(100, 100, 100, 100)];
view.layer.backgroundColor = [[NSColor yellowColor] CGColor];
[self.window.contentView addSubview:view];
NSRect frame = NSMakeRect(10, 40, 90, 40);
NSButton* pushButton = [[NSButton alloc] initWithFrame: frame];
pushButton.bezelStyle = NSRoundedBezelStyle;
[self.window.contentView addSubview:pushButton];
NSLog(#"subviews are %#", [self.window.contentView subviews]);
}
Similar code on iOS should have produced a yellow box and a button, but the code above only produce a button, but the view won't show. Is there something wrong with the code above, and how to make it show the view with a yellow background?
Use setWantsLayer: method of NSView class.
NSView *view = [[NSView alloc] initWithFrame:NSMakeRect(100, 100, 100, 100)];
[view setWantsLayer:YES];
view.layer.backgroundColor = [[NSColor yellowColor] CGColor];
[self.window.contentView addSubview:view];
NSRect frame = NSMakeRect(10, 40, 90, 40);
NSButton* pushButton = [[NSButton alloc] initWithFrame: frame];
pushButton.bezelStyle = NSRoundedBezelStyle;
[self.window.contentView addSubview:pushButton];
NSLog(#"subviews are %#", [self.window.contentView subviews]);
To expand on the suggestion by Kevin Ballard, the classic way to do this is to subclass NSView and override the -drawRect: method. NSRectFill is a very convenient function for filling a rectangle without having to create a bezier path:
- (void)drawRect:(NSRect)rect
{
[[NSColor yellowColor] set];
NSRectFill(rect);
}
NSViews in Cocoa are, by default, not layer-backed. I suspect that if you type
NSLog(#"%#", view.layer);
you will see that it is nil.
In iOS, all views have layers. But on OS X, views don't have layers. In addition, there's 2 "modes" of layer-backed views on OS X. There's what's called a "layer-backed views" and a "layer-hosting view". A layer-backed view uses a CoreAnimation layer to cache drawn data, but you are not allowed to interact with the layer in any way. A layer-hosting view uses a CALayer that you explicitly provide, and you may mess with that layer all you want. However, with a layer-hosting view you may not add any subviews, or use the built-in NSView drawing mechanism. A layer-hosting view must only be used as the root of a CoreAnimation layer hierarchy.
Given all this, you should probably avoid using CoreAnimation at all for your view.
It's possible that an NSBox will do what you want. You can certainly set a fill color there, turn off the border, and set the style to custom. I'm just not 100% certain it will draw as a simple filled rectangle of color. Alternatively you can define your own NSView subclass that draws a color in -drawRect:.
I have the following code that creates a UIView that I assign to my UITableViewCell's selectedBackgroundView property. Everything works as expected, with the exception of the subview's background, which is transparent.
I use the same code to create a custom view that I assign to backgroundView, and that works fine.
What is causing that subview to be transparent for selectedBackgroundView, and how can I avoid that?
- (UIView*) makeSelectedBackgroundView
{
// dimensions only for relative layout
CGRect containerFrame = CGRectMake(0, 0, 320, 40);
UIView* containerView = [[UIView alloc] initWithFrame:containerFrame];
containerView.autoresizesSubviews = YES;
// dimensions only for relative layout
CGRect subframe = CGRectMake(5, 5, 310, 30);
UIView* subview = [[UIView alloc] initWithFrame:subframe];
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
subview.backgroundColor = [UIColor redColor];
subview.layer.cornerRadius = 5;
subview.layer.borderWidth = 2;
subview.layer.borderColor = [UIColor greenColor].CGColor;
[containerView addSubview:subview];
return containerView;
}
As we can see from name of ivar selectedBackgroundView, this background shown by cell when it was selected.
I've to reload few methods (– setSelected:animated: and – setHighlighted:animated:) of UITableViewCell subclass to reset background color of subviews back to their values. Look's like UIKit do some magic in this template methods (iterating over all UIView subclasses and set their background to clearColor)
This code might be helpful for you:
UIImageView *cellImageView = [[UIImageView alloc]
initWithFrame:CGRectMake(0,
0,
cell.frame.size.width,
cell.frame.size.height
)];
cellImageView.contentMode = UIViewContentModeScaleAspectFit;
// normal background view
[cellImageView setImage:[UIImage imageNamed:#"*<ImageName>*"]];
[cell addSubview:cellImageView];
[cell sendSubviewToBack:cellImageView];
[cellImageView release], cellImageView = nil;
Here cell is an object of custom UITableViewCell.
Also you can set backgroundColor property.
I would try to set the alpha for both containerView and subView to 1.0
[containerView setAlpha:1.0];
...
[subview setAlpha:1.0];
this should make your controls totally opaque.
You could also create some images for the background and use that images in state of creating 2 views. Let's say you create 2 image (normalBackground.png and selectedBackground.png) and then set this images as cell background. Here is a nice tutorial.
Try setOpaque:YES on your views.
In the end, I ended up subclassing UITableViewCell which contained a custom view object, and that worked.
I am adding a custom UIImageView to my UINavigationBar using this code:
UIImageView *background = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
[background setImage:[UIImage imageNamed:#"nav_background.png"]];
[background setTag:kTagForReference];
[self.navigationController.navigationBar insertSubview:background atIndex:0];
I then add a custom title using this code:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 400, 44)];
...
[self.navigationItem setTitleView:label];
[label release];
But on some of my pushed views the title is being hidden (or isn't visible). I can't seem to push more than two views onto the stack without the title disappearing.
I've tried to force my UIImageView background to the back of the bar but that still doesn't help. I've printed the subviews of the UINavigationBar and I can see that the UILabel is there, I just can't see it!
Any help would be greatly appreciated.
If you can run the dumpWindows() function, the output would show the view hierarchy so you can see what view is covering it up.
You most likely want to use a UIToolBar instead of a NavigationBar. Then you can add the subviews to the UIToolbar.
Bring the UILabel to the front most view.
[self.view bringSubviewToFront: label];
I'm sure this is a matter of me not knowing how to phrase my question, but I'm at a loss.
I'm working on an app with 2 subviews that will be on the top of the screen at all times (taking up a total of 114 pixels including the status bar). I want the rest of the space to be taken up with a UITabBar and it's child views.
The closest I've been able to get is a tab view that is sized correctly but is positioned at 0,0 so it's under my persistent subviews.
// Create the main toolbar
toolbar = [[UIToolbar alloc] init];
[toolbar sizeToFit];
toolbar.frame = CGRectMake(0.0, 20, 768, 44);
UILabel *textLabel = [[UILabel alloc] init];
textLabel.frame = CGRectMake(240, 20, 300, 40);
textLabel.textAlignment = UITextAlignmentCenter;
textLabel.backgroundColor = [UIColor clearColor];
textLabel.text = #"Character Name";
[self.window addSubview:textLabel];
[textLabel release];
...
// Create the stats bar
StatsViewController *statsView = [[StatsViewController alloc] init];
statsView.view.frame = CGRectMake(0.0, 64, 768, 50);
[self.window addSubview:statsView.view];
// Create Tab Bar Controller and Tab View Controllers
tabBarController = [[UITabBarController alloc] init];
...
[self.window setRootViewController:tabBarController];
[tabBarController release];
[self.window addSubview:toolbar];
[self.window bringSubviewToFront:statsView.view];
[self.window bringSubviewToFront:textLabel];
[self.window makeKeyAndVisible];
return YES;
A tab bar controller (or any view controller, for that matter) will always try to resize its view so that it takes up the entire bounds of its superview. So if you want the tab bar controller to not do that, I would create another plain view controller that becomes your new root view controller.
In that view controller's view, you create 2 container subviews, one for your statsView and one for the tab bar controller. Now you add the tab bar controller's view to the one container view and the StatsViewController's view to the other.
(Note: I did not test this.)
So, my iPad program has a pseudo-split view controller (one that I implemented, not base SDK one), and was working correctly a while ago. It has the basic layout (UINavController for master, content view controller for detail on right), but I have it so the master view doesn't disappear when rotated into portrait view.
Recently, I added in a UITabBarController to contain the entire split view, which has made the navigation bar go wonky, while all the other views are positioned fine. In addition, the navigation bar only gets mispositioned when the program starts up while the iPad is in landscape, or upside-down portrait. If it starts out in portrait, everything is fine.
Example images can be found here:
http://profile.imageshack.us/user/Pzychotix
Image where the navigation bar is upwards is when I initially launch the program.
Image where the navigation bar is downwards is after I rotate once or more times.
Relevant Code:
RootViewController.m:
- (void)loadView {
navController = [[NavigationBreadcrumbsController_Pad alloc] init];
ABTableViewController_Pad * tableViewController = [[ABTableViewController_Pad alloc] initWithNibName:#"ABTableView"];
master = [[UINavigationController_Pad alloc] initWithRootViewController:tableViewController];
[tableViewController release];
// Dummy blank UIViewcontroller
detail = [[UIViewController alloc] init];
detail.view = [[[UIView alloc] init] autorelease];
[detail.view setBackgroundColor:[UIColor grayColor]];
self.view = [[[UIView alloc] init] autorelease];
self.view.backgroundColor = [UIColor blackColor];
[self positionViews];
[self.view addSubview:navToolbarController.view];
[self.view addSubview:master.view];
[self.view addSubview:detail.view];
}
// Handles the respositioning of view into it's current orientation
-(void)positionViews{
CGFloat tabBarOffset = 0;
if(self.tabBarController){
tabBarOffset = self.tabBarController.tabBar.frame.size.height;
}
if(self.interfaceOrientation == UIInterfaceOrientationPortrait || self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
self.view.frame = CGRectMake(0, 0, 768, 1004);
navController.view.frame = CGRectMake(0,0,768,44);
//adjust master view
[master.view setFrame:CGRectMake(0, 44, 320, 1024 - 44 - 20 - tabBarOffset)];
//adjust detail view
[detail.view setFrame:CGRectMake(321,44, 448, 1024 - 44 - 20 - tabBarOffset)];
}
// Landscape Layout
else{
self.view.frame = CGRectMake(0, 0, 748, 1024);
navToolbarController.view.frame = CGRectMake(0,0,1024,44);
//adjust master view
[master.view setFrame:CGRectMake(0, 44, 320, 768 - 44 - 20 - tabBarOffset)];
//adjust detail view
[detail.view setFrame:CGRectMake(321,44, 1024 - 320, 768 - 44 - 20 - tabBarOffset)];
}
}
Well I've found a solution, though I'm still scratching my head as to why it's working.
Basically, I called layoutIfNeeded on my UINavigationController, and that fixed everything right up. What I don't understand is why it was working before, or why I would need to call layoutIfNeeded, as I assumed setFrame would automatically deal with laying out any subviews of controllers.