iPhone Dev - UIView frame and loadView - iphone

I'm learning to develop for iPhone (programmatically, I don't like IB). I only use IB for the frames of things, so I design in IB but only use it to check each control's frame. Something I've gotten very confused about is the frame of a UIView. First of all, if you do not implement loadView in a view controller, it automatically creates an empty UIView (and I've noticed that its black) and assigns that to self.view, correct? What about the frame? does it automatically figure out what the frame should be? I mean, it needs to be different depending on the if there is a status bar, if there's a tab bar, a toolbar, the orientation. In my tab bar app, it seems no matter what I set frame of my uiview is, it still looks fine. And also, the frames for UIView seem messed up in IB. Like the y value of a UIView taking up the whole window (except status bar) is 0, when it should be 20, because the status bar takes up the top 20 pixels. And in IB a view in a tab bar controller has a y value of 411, even though it begins right under the status bar. And whenever I NSLog a frame (the four numbers, view.frame.origin.x, view.frame... etc.) the x, y, width and height are always 0.. Can someone who understands it please explain it to me? Thanks!!
Ok, this is weird... This:
CGRect test = CGRectMake(0, 20, 320, 460);
NSLog(#"%d %d %d %d", test.origin.x, test.origin.y, CGRectGetWidth(test), CGRectGetHeight(test));
gives output:
0 0 0 1077149696
??? What's with that?

First thing you should note is that CGRect's origin and size members use float, so in your NSLog statement you need to use %f, not %d. So for example,
NSLog(#"%f %f %f %f", test.origin.x, test.origin.y, test.size.width, test.size.height);

Pixel sizes are floats because on the desktop you need precise math when doing scaling and want to support proportional sizing. If you stick to integers and the window size is expanded by, say 1.2x, integer round-off will throw things out of line. They kept the same thing on the iPhone because you can have subviews that get auto-sized and floats offer more control over size and position.
As for the view positions, if you go into IB and select a view and check the "View Attributes" inspector you'll see one of the options is to show "Simulated Interface Elements." By default it has the Status Bar enabled (with Gray) and it allows for its height in the interface. If you check the size tab in the inspector, the view height is actually set to 460 (allowing for the 20 pixel status bar).
You can choose to include a top bar or a bottom bar and it will "simulate" those by adjusting the content height for you. This is just a positioning simulation so you can lay out your controls. At runtime, it's assumed you'll load the view into the proper viewcontroller which will have the proper chrome so it should all look right.
A lot of IB numbers are there for relative positioning. When you add the view to a navcontroller or a tabview, this view becomes a child of those parent views and by default they autoresize subviews. So if you've "simulated" for that top bar in IB, your controls should all be lined up properly and flow into the right place and you should see what you expect (of course, depending on what parent control ends up with your subview as a child).
BTW, you're missing out a lot of convenience by not using IB to wire up the controls and connecting them to IBOutlets. It really helps take out a lot of manual effort and extra code. It's like Superman refusing to use his X-ray vision and instead deciding to punch through walls :-)

Answer: IB doesn't always show correct origin/location coordinates, and some things, like UITabBarController, resize the view to be what they want it to be no matter what you set it.

Related

Auto Layout doesn't work as expected

Given:
a 'logo' ( uiimageview with vertical space >= 10 from top of super view) and a 'log in' and vertical spaces between logo and holder view is <=31
Goal:
in the lanscape mode, logo will move up to the top due to a change from screen'size and holder view should be moving regarding as well.
However, when I rotate from portrait to lanscape, what I am getting is that the vertical constraint still stays at 81 ( somehow i can not delete this value from xcode ). Moreover, the vertical space between a 'Main View' and 'Log in' can not be deleted as well. I know I am doing something wrong here.
Please help if you have any suggestions about this problem.
Details:
+picture 1 is the portrait mode with some details about the constraints
+picture 2 is the lanscape mode with some errors for it.
Edited : Like the way I set up I want the vertical space of logo can be shrinked but it is always >= 10 from the top of the main view
I don't know if this can be done all in IB -- if so, I haven't figured it out yet. I've done it in code as I show below. I start with a constraint from the top of the image view to the top of the main view with a fixed value and also constraints between the image view and the login view. This should be enough to satisfy the system, and you can remove any other constraints to the top or bottom of the main view (I used buttons in my test which have an intrinsic height, so I didn't need to set that. If your views don't have an intrinsic or specific height set, you would have to do that also). Then, in code I remove that constraint to the top (IBOutlet conTop), and remake it to the bottom:
#implementation ViewController {
IBOutlet NSLayoutConstraint *conTop;
IBOutlet UIButton *button;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.view removeConstraint:conTop];
conTop = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.view attribute:NSLayoutAttributeBottom multiplier:.5 constant:-120];
[self.view addConstraint:conTop];
[self.view layoutSubviews];
}
By using a multiplier and a constant together, you can adjust how it moves after a rotation. The numbers I chose here looked pretty good to me, but you can mess with them to see what they do. You can think of the multiplier as a sensitivity factor -- the smaller that fraction, the less change in the distance from the top you will get on rotation. If you need exact values, you can use a little algebra to calculate the values for the multiplier and constant.
You need to find a way to express the relationships declaratively – and ideally in terms which work for both cases.
You haven't quite said what you want to happen in landscape mode. The logo at the top, but how far from the top? Where do you want the holder view?
There's a WWDC video which is essential for understanding how Auto Layout works. Have you had a chance to watch it yet?

Strange black bars in UITableView

I'm really scratching my head over this one: When I make visible the Toolbar of the navcontroller in my view hierarchy, strange black bars appear at the top and bottom of the table view. http://grab.by/8pgz
The app is universal, and on the iPhone simulator (or device) this does not show up, although they use the same UITableViewController subclass.
I cannot even figure out what the extra bars are, which makes it even harder to come up with a solution.
They are, however, not navbar / toolbar. They are not section or table view header / footer. It does not merely seem to be a resizing of the table view either, since its background is not black.
The custom table view controller sits inside a UINavigationController, which in turn is inside a UISplitViewController. Pretty standard, as far as I understand.
Like said, they do not show up on the phone - nor do they appear in the popover in portrait orientation.
I'm really out of clues here. Any suggestions would be most welcome.
Cheers,
Gregor
Sweden
Update: Problem solved by moving the code for making toolbar visible to -(void)awakeFromNib method instead of -(void)viewDidLoad. Still don't know what went wrong, but now at least it works.
I believe there is a problem with the frame you're trying to use to initialize the tableView.
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, height, width);
Try changing the height width variables in code, to reflect your height & width on the iPad.

Hiding elements outside of bounds on iPhone

I'm trying to get a UIView to expand (with animation), sort of like an accordion menu. I can get the animation working fine however the issue is the subviews of the UIView are expanding past the bounds of the UIView.
For example, the view has an UILabel in the top-right corner with a height of 16. Assume the UIView height is 0 at the beginning of the animation. One would expect that the content of the view be hidden and gradually reveal as the UIView grows. Once the height has reached 8, for example, half the label should be visible. However that isn't the case - rather, the label is visible the whole time, regardless if it's height extends outside of that of it's parent view.
Any ideas how to resolve this?
Okay, I had to set the clipsToBounds property to true. I spent some time googling before making the question but didn't have much luck until I saw it in the Related section of my question.

how do i work on a taller view in interface builder?

OK, so I am learning to use UIScrollView in interface builder. since the scrollView itself does not have any content, I created another view, the contentView, to hold my controls and scrollable content. into this view I place controls, labels, etc and then in my code i set the contentSize of the scrollView to the size of this contentView.
My question seems stunningly simple and so obvious that I must have missed something somewhere. when I created this XIB in IB I got your standard empty iPhone interface window. I dropped a scrollView on top of it, it took up the whole window. I dropped a view on top of that, it took up the whole scrollview. I added some controls, which so far I can still see inside the contentView rectangle in IB.
My question is how do I work on/add controls which lie outside of the visible part of the contentView in IB? LOL. it seems so simple, but i just don't get it. I can set the height of the content view or drag the rectangle to whatever I like (and indeed this is the whole point of having a scrollview) but the fixed UI window from IB won't expand so i can see the "offscreen" part of the contentView to add more controls. It's like it's just fixed at that size because that's the size of one iPhone screen and it won't let me make it any bigger/taller.
what did I do wrong?
-a
You need to turn off all simulated user interface elements (like the status bar) to be "undefined" except for size, which you select to be "Freeform" from the drop-down, and then you can set the view height using the Ruler tab to be whatever you like.
For example, here I've selected a photo view controller, and set the size in the right side bar to be "iPad Full Screen" - but I could also change that to "freeform" to set any height I wished.
First add a ViewController by any which way you prefer. Then in interface builder, click on the view controller Icon (on left). In the connections inspector click on the ruler icon "Show the size inspector". There will be option list for simulated size, change to "Freeform" and increase the height to any size you want. Hope this helps.
neeever mind. you drag the content view up so that some controls are offscreen and then add more controls/expand to the part you just made visible by moving the top stuff off the top. in effect, you physically scroll the contentView in IB by dragging it with the mouse. seems a smidge counterintuitive, but whatever.
Set simulated size to freeform for the the view controller to a large enough size that you can add in your controls
You don't have to create a view to place inside the scrollview if you don't want. In your case, it sounds like it doesn't make much sense.
As for the second part of your question, you can place items directly on the scrollview (it is a view afterall), as subviews. If you want them to be off screen, then just set their frame up to be at those particular x, y coordinates you want it to be at. You will have to ensure your scrollview's contentSize property is large enough though to house your entire content, this is what allows scrolling horizontally/vertically.
You probably want to do the offscreen elements programmaticly instead of using xibs.

UIView Clipped By Statusbar until Autorotate

Ive created a multiview application that uses multiple controllers to display and control views. The problem Im having is, when the simulator initially loads the view the header is partially covered by the bar at top of screen and the tool bar at the base is not touching the base of the screen. I used the Interface builder size attributes to control the view when the iphone rotates and it works perfectly. All smaps into place perfectly both in landscape and portrait mode AFTER a rotation but the problem is with the initial load before a rotation occurs.
Your thoughts a much appreciated.
Tony
I grappled with this issue for days and days--no amount of fiddling in IB worked.
Ultimately I got it to work by adding this line:
mainViewController.view.frame = window.screen.applicationFrame;
to the application:didFinishLaunchingWithOptions: method. (Where mainViewController is the primary UIViewController).
I've had issues with views being clipped by status, nav, and tab bars. I've fixed most of them by using the Simulated Metrics feature in Interface Builder. That way what your laying out in IB is a lot more accurate to what your actually going to get.
I ran into this issue too. Specifically, when displaying an ADBannerView, my whole view would shift and be under the status bar and leave a little empty space just the size of the status bar at the bottom of the iPhone screen. Here's how I solved it : (Adam's answer here helped me figure this out):
// In the function that displays an iAD Banner
CGRect contentFrame = self.view.bounds;
CGRect myStatusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGFloat statusHeight = myStatusBarFrame.size.height;
// Set the view's origin to be under the status bar.
contentFrame.origin.y = statusHeight;
I needed to set the origin of my view to be below the Status bar, and that solved the issue for me.
The problem is that you're adding your controller "incorrectly" according to Apple docs (although IMHO Apple designed it badly - the default should be that you don't need to shift!)
if you're going to have a status bar, Apple requires that you "manually" shift all your controllers down by 20 pixels (more accurately, by the height of the statusbar - although that's always 20 pixels today, Apple lets you request the height at runtime, from the "statusBarFrame" property in UIApplication)
Apple's classes - e.g. UINavigationController / UITabBarController - automatically shift themselves down by 20 pixels when they're added to the screen. Both classes have a bug where they will do this shift even if they are not the main controller - so your app suddenly shifts down an extra 20 pixels, leaving 20 pixels of white space at top.
But, when they rotate, those classes often "get it right" and move back into place. Or vice versa.
c.f. this link for a much more detailed explanation of what's going on, and how to write your code the way Apple wants you to:
http://blog.red-glasses.com/index.php/tutorials/iphone-auto-rotation-for-intermediate-developers-tutorial-1/