SubView doesn't want to fit into specified frame - iphone

The the problem arises when i dynamically load subview from nib and add it to the current main view during viewDidLoad.
UIView *someView = // load from nib
someView.frame
= CGRectMake(.0,
.0,
kFormSheetWidth /* = 540.0 */,
kFormSheetHeight) /* = 576.0 */;
[self.view addSubview:someView];
It all happens inside FormSheet window, where UINavigationController instance was passed into presentModalViewController:.
But, someView always resizes itself to be by 220.0 larger and by 116.0 higher and thus doesn't fit into window.
someView nib looks like
UITableView
xx UIView
xxxx UITextInput
xx UIButton
All these views have autoresizeSubviews == YES, clipSubviews == YES.
It's very surprising behavior. Interestingly, when I set the frame of someView in viewWillAppear:, everything looks fine. What (where) should I look for to tackle this thing?

Are you doing this is landscape mode? (ipad I assume because of the dimensions?)
This is a bit of a stab in the dark, but its a problem for a lot of people. (if its not it I appologize)
UINavigationController's rootview doesn't handle the
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
method like your code does, so you need to subclass UINavigationController and implement it like your view controllers so it doesn't always add the views in portrait.

Some view is resizing itself after you create its frame on the viewDidLoad.
What you should do, is set the autoresizeSubviews to NO or just set it's frame in the viewWillApear.

Related

Why is self.bounds.size.height not taking into account the UINavigationView nav bar in this code?

I have a configuration UITableView that can launch a colour picker via a UINavigationController approach:
[self.navigationController pushViewController:colorPickerViewController animated:YES];
[colorPickerViewController release];
The effect of this means the ColourPicker will have a navigation bar at the top ( and back button)
The structure of the ColourPickerViewControll and it view ColourPickerView is as follows:
- ColourPickerViewController - in it's XIB file at the top level view it has:
- ColorPickerView : UIView (i.e. custom UI view) - in it's methods it has:
- (id)initWithCoder:(NSCoder*)coder {
if ((self = [super initWithCoder:coder])) {
CGFloat currVertBounds = self.bounds.size.height;
The issue here is the the value of currVertBounds is coming to 480, so it's not taking account of the navigation bar
QUESTION: How do I get the true displayed height of the ColorPickerView instance?
Is it something to do with trying to get the layout calculated in the custom UIView, and perhaps the custom view isn't rendered within the overall controll/navigationController at that stage?
You're reading bounds too early. None of the layout/subviewing magic that happens in UIKit will take place during [super initWithCoder:]. You should be reading bounds and laying out your view either:
In viewDidLoad/viewWillAppear, and set up the autosizing parameters for all manually created UI elements so they can get moved around with respect to different interface orientations. In these functions I wouldn't rely on bounds being exactly correct but I would say it's at least the right height:width ratio. It could possibly be in the wrong orientation, though.
In viewDidAppear, if you wish to manually position elements. By the time you hit viewDidAppear all of the resizing and such has taken place, and the only thing left to worry about is future rotations, which you should adjust your view for in willAnimateRotationToInterfaceOrientation:duration:.
Docs on willAnimateRotationToInterfaceOrientation:duration: state:
By the time this method is called, the interfaceOrientation property is already set to the new orientation. Thus, you can perform any additional layout required by your views in this method.
iOS 5.0 docs add the clause:
...set to the new orientation , and the bounds of the view have been changed. Thus...
I think this is still the case for prior iOS versions.
Views for iPhone will by definition have a height of 480 pixel when created in Interface Builder, so that's what you're seeing when you're initializing the view. Once the view has been properly initialized and added to the view stack, it will be resized to match the actual space available.
You're simply asking the view of its height before it has been adjusted.
Try reading the height in viewDidLoad, then it should be correct.
Yes, this is right because the instance of UINavigationController is declared in your (delegate.h) & that navigationBar is added to window not on your self.view
When you check the bounds of your current view, it should definitely return 320 * 480.
It should not include height of navigation bar in you view, as it not on that view. Its on window.

UIViewController frame

I've got the following setup.
A pagingScrollViewController with a UIScrollView as it's view (self.view = scrollView).
In that scrollView I add the view of a rootViewController as subview.
The view of the rootViewController is an UIView on which I add one or the other view (depending on internal logic).
The UIScrollView is initialized with a frame equal to the applicationFrame, so 0, 20, 320 , 460. Which is fine because of the statusbar on top.
The problem comes when adding the rootViewController's view to the scrollview. In the creation process of the rootViewController it's view is setup in -loadView which is still commented out in my case. So the standard appel loadView is processed.
It gives me a view which also has a frame of 0, 20, 320, 460. Which is not as I would like to see it as now the View of the rootViewController is 40 pixels removed from the top of the actual iphone screen.
Now, this is of course easily fixed by manually setting the rootViewControllers view in -loadView or assigning it a frame where the y is set to 0 in -viewDidLoad;
But I cannot imaging that that is the way apple intended this to be done. I'm not using Interface builder, so maybe that's the problem, apple intends me to use IB, but im stubborn on that subject :)
Am I using viewControllers the wrong way? This seemed a nice way to split the logic of the scrollView away from the logic of the other view's.
What would be the correct way to add the view of a second UIViewController to the view of a first UIViewController. In which the first viewController.view receives a frame of 0, 20 , 320, 460. And de second controller.view a frame of 0, 0, 320, 460 (so basically the bounds of the first controller.view)
There's a lot of use of the word controller and view in there, hope the question is clear though.
To answer my own question...
Ravin was right.
After reading up on the loadView method and apple's view controller programming guide I've come to the conclusion that this is the default behavior of the system. If you want anything else, override -loadView and do it yourself. Actually apple states that if you don't use an NIB, you SHOULD override -loadView to create at least a basic simple view.

layoutsubviews only called once despite any device rotation

just what the title says: I create an empty UIView subclass and I add a subview to it, I create an empty layoutSubviews method and I add a breakpoint there. Then I build the project and the simulation stops when loading the view at that point in the layoutSubviews method. Everything is fine so far. I continue the simulation and I rotate the device (assuming that the view controller is set to allow any orientation) but layoutSubviews is never called again, despite I can see how my object is rotating.
Any idea?
OK, i will simplify my question: How do I create a custom UIView with some subviews and make it responsive to the device orientation? I don't need to use drawRect or anything, just subclass UIView, add a couple of subViews and handle them when the device is rotating.
Did you try setting the autoresizingMask on the UIView? For example like below:
someView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
From the UIView documentation:
'When a view’s bounds change, that view automatically resizes its
subviews according to each subview’s autoresizing mask'
If you set the autoresizingMask to something other than None it should mean that the layoutSubviews method is always called when the views bounds change.
I'm not sure if it's exactly the same but i ran into a similar problem. I was using a UISplitViewContoller (template from Xcode) and adding a subview into the DetailViewController. The layoutSubviews methods was not getting called on rotation because it was a subview of the main UIView in the ViewController. So i added this to the DetailsViewController to get it to work (detailView is my subview):
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
if (detailView != nil) [detailView layoutSubviews];
}

UIView Subview Does not autoresize on orientation change

In my iPhone app, I have a view controller with two views (essentially, a front & back view). The front view is the main UIView, and the back view is a secondary UIView which is added as a subview using [self.view addSubview:backView] when showing the back and [backView removeFromSuperview] when hiding it. However, when the orientation changes, I have the following issue: the main UIView (frontView) rotates & all of its elements resize properly, but the secondary/subview UIView (backView) does not rotate & all of its elements do not resize properly. Does anyone have suggestions on how to make the secondary UIView autoresize properly according to the rules I have set in Interface Builder?
In the end, the solution I found was simply to separate my UIViews into separate UIViewControllers, and make sure that any views that I wanted to be able to rotate only had one UIView.
If I understand correctly, at the time of rotation 'backView' has been removed from it's superview, yeah? If so, that's the cause of the problem. The autoresize property determines how the view resizes relative to it's superview. If it doesn't have a superview it won't resize.
Perhaps using [backView setHidden:YES] instead of [backView removeFromSuperview] will be sufficient for your needs.
I had the same problem, here is how I fixed it based on imaginaryboy's
suggestions (thanks!)
Add the backview to the viewcontroller at viewDidLoad and hide it at the same time. Show it when needed, Hide it again. Set the resizing of the backview to UIViewAutoresizingFlexibleWidth in IB (or code I guess, I used IB)
Not that this is the same problem, but I found a similar problem when adding 2 subviews in my application:didFinishLaunchingWithOptions method. Since your reference above is using [self.view addSubview:view], I would understand that to mean that self is not your UIWindow. When adding an additional view controller to your App Delegate window (UIWindow), the second view controller will NOT receive any rotation events and will never rotate. Only the first view controller added to UIWindow will rotate. See:Technical Q&A QA1688 I believe this also affects views added after the first view where the first view is later removed from the superview.
I ended up following the suggestion I read elsewhere to use separate views for each orientation, thereby eliminating the need to worry about resizing behavior. As always, YMMV.
Or; if you want to avoid an additional controller, you can achieve the same effect by setting view.frame in willRotateToInterfaceOrientation:: like so
if(UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) ;//set stubborn view.frame for landscape orientation
else ; //set stubborn view.frame for portrait orientation
Although it feels like a hack; it's simple.

How to achieve smooth animation when using single-step rotation / How do I get the new frame size at the start of rotation?

I'm trying to switch from two-stage rotation to one-stage rotation (to avoid the console warning, and because Apple recommend doing so because one-stage is faster).
However I can't figure out how to get the new size of my view (taking into account the navigation bar, status bar, etc) early enough to perform the update of my UI during the animation (rather than simply snapping the items to their new positions at the end as many applications seem to do, which results in a big "jerk" right at the end of the animation).
The sizes I get in the willAnimateRotationToInterfaceOrientation:duration: method are (perhaps obviously) the old sizes.
I can see I should be able calculate it by hand, working out the current bar heights, then inferring the new view frame size by deducted those from the rotated screen dimensions? (which isn't that difficult to do, though is perhaps fragile as it assumes the navigation bar, status bar, etc will be the same height in both orientations, and you'd have to manually take account of the toolbar being different heights in portrait vs landscape - I just want to make sure I've not missed a more straightforward or common way.)
Any feedback on approaches other people have taken would be great!
Thanks
Joseph
I've had the most success using my view's layoutSubviews method for autorotations. When layoutSubviews gets called during an orientation change, the view's bounds are already set to what they will be at the conclusion of the rotation. You can also at this time query the status bar, navigation bar, and toolbar for their sizes and get the correct post-rotation values (although the status bar width and height may be swapped--I just take the smaller value as the height, works great). Using that information you can lay out subviews and they will then be animated as part of the rotation.
It can be annoying to create a UIView subclass for every situation where you want to do this, so I created a special subclass of UIView called DelegatingView, which looks like this:
#protocol DelegatingViewDelegate
- (void)layoutSubviewsForView:(UIView*)view;
#end
#interface DelegatingView : UIView {
id<DelegatingViewDelegate> _delegate;
}
#property (nonatomic,assign) id<DelegatingViewDelegate> delegate;
#end
#implementation DelegatingView
#synthesize delegate = _delegate;
- (void)layoutSubviews {
[super layoutSubviews];
[_delegate layoutSubviewsForView:self];
}
#end
I use this as my base view, add subviews to it, and set my UIViewController as the delegate. Then I can layout subviews from my view controller, having access to the current orientation and so on.
Which OS version are you targeting? Under OS 4.0 (which is what I did a quick test in), [view bounds] within willAnimateRotationToInterfaceOrientation:duration: returns the new, post-rotation bounds.
If you are not seeing that, I’d suggest double-checking that your view has the appropriate auto resize mask set.
If you have complex layout requirements in the view of your view controller, it is worth it to create a subclass of UIView and perform your layout code in -layoutSubviews (the correct place to do view layout). As Hilton Campbell pointed out, if you use this method for layout, you can check frame sizes and subview relationships and set their new positions appropriately.
If your view controller's view has a simple set of subviews, then you should probably just set their autoresizingMask properties appropriately, and let them animate themselves automagically.
I tried two methods to adjust view's size.
1.) notify child views to adjust their size after -(void)viewWillAppear;
The drawback is that it will be executed each time when viewWillAppear.
ex:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self notifyChildViewWillAppear:animated];
}
at child view
- (void)notifyChildViewWillAppear:(BOOL)animated
{
// at this time, the size of superview's bounds
//equals the content view's size
// (no navigationBar's height and toolbar's height.
self.frame = [self superview].bounds;
}
2.) Calculate the necessary size by hand.
I calculate as viewDidLoad.
The sizes of navigationBar and toolBar can be derived by following code.
self.navagationController.navigationBar.view.frame.size.height;
self.tabBarController.tabBar.frame.size.height;