layoutsubviews only called once despite any device rotation - iphone

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];
}

Related

UIScrollView as a subview does not scroll

When I set my UIScrollView to be the main view within Interface Builder, the scrolling works as expected. However, when I add the UIScrollView as a subview of UIView the scroll does not work.
So it seems my trouble begins when the UIScrollView is added as a subview to UIView.
The "User Interaction Enabled" it true for both view.
I though perhaps I might have to do something with in touchesBegan something lake passing the touches to the UIScrollView, but have not had much luck with that.
Has anyone seen this before?
scrollView.delegate = scrollView; //or nil may work
It's always good to show a complete working code snippet:
// in viewDidLoad:
//UIScrollview *myScrollView;
UIView_descendent *contentView;
// scrollview won't scroll unless content size explicitly set
[myScrollView setContentSize:contentView.frame.size];
I have not found a way to set contentSize in IB.

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;

SubView doesn't want to fit into specified frame

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.

Infinite loop when adding CATiledLayer to UIView

I have a UIView in which I add a CATiledLayer and implement 'drawLayer'.
If I use a UIViewController and add the layer to a new subview of the controller, then everything is ok.
If I however try to use a UIView to and do all the craetion and drawing within this, then I get a infinite loop at the point shown below when I add this view to a superview.
0x002cfafb <+0425> ja 0x2cfa23 <-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]+209>
What am I missing?
Update:
By removing 'layer.delegate = self', the code no longer crashes...
Just found this:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/18121-calayer-interesting-crash.html
"You can't set the delegate to be the same as the view's layer's delegate, which by default is the view itself"