Autolayout - Freeform View - uiview

I have a UIViewController with a UIView in which I am adding UIViewController views in as subviews. i.e
UIViewController *vc = [[MyViewController alloc] init];
[my_view_in_xib addSubview:vc.view];
The ViewControllers view and the subviews have AutoLayout enabled. The top level does resize correctly (changing background color to red) I can see it is the right size. However in the subviews in the XIB file I have set the view to be "Freeform" or "None" on the "Size" and pinned the tables to top & bottom and made them pin on the width but the height does not seem to auto resize?
Any suggestions?
James

Every time you resize, you should send setNeedsLayout to the superview. Otherwise, the layout system will not recalculate the frames of the subviews.
In Apple's own words:
View resizing primarily occurs when the orientation of your
application’s interface changes but it may happen at other times as
well. For example, calling the setNeedsLayout method forces your view
to update its layout.

Related

Positioning a UIPageControl on a UIViewController with respect to the position of a UIView on it's subViewController.

So I've got a problem with layout and constraints.
I'm using the app coda tutorial for UIPageViewControllers at http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/ but I've made some small changes.
I have added a content view controller as subview to my main view controller. It matches the height and width of its superview. On this subview I have laid out a UIImageView and a UISegmentedControl (see screenshot). I have also added some constraints to this view controller, which ties the UIImageView to the top,right and left of its container, also the segmented control is centred on the image view and added constraints to the uiimageview above and the container bottom below. The effect of this is that the view controller maintains its appearance on different device sizes.
However, I need to position a UIPageControl (page indicator) at the bottom of the UIImageView but I do not want to place it on the subview because then it would turn with the pages of the UIPageViewController - so I position it on the SuperView (see screenshot). I use;
[self.view bringSubviewToFront:self.pageControl];
to bring my page control always to the front of the view hierarchy. The final result should look like the below screenshot,
however the relative location of the page indicator on the superview changes for different devices.
My question is how can I add constraints to this page indicator on it's own viewController so that when the page indicator is positioned on the superview it always appears at the bottom of the UIImageview which is positioned on the subview no matter what device it is run on.
Any help would be greatly appreciated.
so I found a simple answer....
just add a new UIImageView to the SuperView and set similar constraints so that it obeys the same scaling laws as the UIImageView in the subview. Then use layout constraints to anchor the page indicator to the bottom of the new UIIMageView.
see screenshot.

Adding UIViewController with ScrollView and AutoLayout to another UIViewController

I have a UIViewController (added to a UINavigationController) that adds another UIViewController's view using standard code:
[self addChildViewController:toVC];
[self.view addSubview:toVC.view];
[toVC didMoveToParentViewController:self];
That's working fine.
The childViewController's (toVC) view is constructed using Interface Builder and Auto Layout, and it contains a UIScrollView. If I load toVC into my app directly into a UINavigationController (instead of adding it to another view controller) scrolling works perfectly.
However, when adding toVC to my mainVC using the above code, toVC's scrollView doesn't scroll at all and I'm at a loss as to what I need to do.
You should set the frame and the autoresizing mask / auto layout constraints of the child view controller's view. Even though the child controller's view is set up with auto layout, it still needs to be positioned in the parent view controller's view with whatever system the parent controller is using.
EDIT: Another potential issue, since you mentioned that the scroll view doesn't scroll when presented:
Did you set top, left, right, and bottom constraints for the scroll view's subviews? Scroll views treat "space to superview" constraints differently than normal views do; rather than defining where its subview is positioned, these constraints define the content size. Failing to set constraints on both top and bottom, or both left and right, may leave the scroll view with a content size of {0, 0}, in which case the scroll view would not scroll.

Using Auto layout with hidden views

I have a view controller with a UIScrollView. Inside that scrollview I have two UIViews. viewA sits onto of viewB. I am using Xcode 5 and auto layout in this project. What I would like to do is when viewA is hidden, move viewB to the top where viewA was sitting (technically, still sitting, just hidden).
I looked at this SO answer: How to use auto-layout to move other views when a view is hidden?
However it hasn't really helped me. I connected a IBOutlet which was a constant to the vertical spacing to the top of the scroll view and set it to 0.0f when viewA was hidden. However it makes no changes to my UI at all.
First get the Top Space to SuperView NSlayoutConstraints Outlets for both subViews and then do the following:-
[self.aView setHidden:YES];
if([self.aView isHidden])
{
self.bViewTopConstraint.constant = self.aViewTopConstraint.constant;
}
using this the second UiView will go to the place of first UIView.
For Scrollview you have to set the constraints value properly. No need to set any contentsize. Once you set the constriants scrollview will work automatically.
Check the attached screenshot.

how to make a programmatically generated subview conform to the bounds of the superview (iOS)?

I have a containing view called containerView which is a UIView. When the app starts, it's just that view. During the course of the app's execution, I want to swap two "full-size" sub views in and out of the main containerView.
My question is, how do I make sure that the sub views fill up the entire containerView regardless of the orientation of the iPad. The containerView is 300 wide, but the height varies based on orientation.
I've tried:
setting the frame of the subview's from viewDidLoad, viewWillAppear, viewDidAppear directly equal to the side of the containerView frame,
creating LayoutConstraints that force the subview to conform to the containerView's proportions from viewDidLoad, viewWillAppear, and viewDidAppear. The question here is when to apply these constraints, and since the view is removed periodically, when to reapply the constraints.
I don't know what the idiomatic approach is and I want my code to be maintainable and reusable. Am i approaching this the wrong way? Is there some other way to make sure a subview fills up its containing view?
Example Code:
https://gist.github.com/Sahasrara/6817105
You should set the frame size in viewDidLayoutSubviews. By then all the autolayout nonsense has occurred.

Need to change UIScrollView contentSize when portrait xib is adjusted to landscape

I've got a UIViewController with an iPad xib in portrait orientation. When my iPad is in landscape orientation and I put that view controller into my UISplitViewController's detail pane, it gets automatically resized to fit which is great.
However, when I'm configuring my views in -viewDidLoad, the final size of the views is not yet in landscape orientation/size.
The -didRotateFromInterfaceOrientation: method and the other methods associated with it do not get called when the UIViewController is loaded already in landscape, so when and where am I able to properly set the contentSize of the scroll view and it's contents?
I only want my scroll view to scroll vertically, so I want to make sure that my contentView inside the scrollView is re-fit to the width of the view controller in whatever orientation it is in.
No matter what .frame or .bounds I check, whether it's on my view, my scroll view, or even the detail view controller of my SplitViewController show me what my ACTUAL size is. When in landscape using a UISplitViewController, the left hand side is 320px wide which means the right hand side shouldn't be more than 704px wide, but whenever I check the frames and the bounds of my view and my scrollview, they report as 768px wide which is not correct.
My scrollview is CLEARLY only 704px wide because I can see the scroll indicators correctly.
What am I missing?
Here is my code in -viewDidLoad...
CGSize textSize = [self.purchase.textDescription sizeWithFont:self.labelTextDescription.font constrainedToSize:allowedSize lineBreakMode:UILineBreakModeWordWrap];
CGRect textFrame = self.labelTextDescription.frame;
textFrame.size.height = textSize.height;
self.labelTextDescription.frame = textFrame;
CGRect contentFrame = self.contentView.frame;
contentFrame.size.height += textSize.height;
if (contentFrame.size.width > self.scrollView.frame.size.width) {
contentFrame.size.width = self.scrollView.frame.size.width;
}
self.contentView.frame = contentFrame;
[self.scrollView addSubview:self.contentView];
self.scrollView.contentSize = self.contentView.frame.size;
The proper time to lay out your views and set your scroll view's contentSize is during the layout phase of the run loop. During this phase, UIKit sends the layoutSubviews message to any view that has been marked as needing layout and is in a window. A view is automatically marked as needing layout when various things happen, including when it is first added to a window hierarchy, when it is given a new subview, and when its size changes. You can also manually mark a view as needing layout by sending it the setNeedsLayout message.
By the time a view receives the layoutSubviews message, UIKit has already sent layoutSubviews to any of the view's ancestors (its superview and up) that needed it, and it has already had its frame adjusted based on its autoresizing mask or autolayout constraints, and its own subviews' frames have already been adjusted based on their autoresizing masks or autolayout constraints.
If self.view is already a custom subclass of UIView, the best approach is simply to override layoutSubviews in that class. Put your layout code there, and set the scroll view's contentSize there.
If you're not using a custom subclass, and you don't want to create one, then you can do the layout in your view controller's viewWillLayoutSubviews or viewDidLayoutSubviews method, if you're deployment target is iOS 5.0 or later. You can probably guess when these messages are sent. :)
During autorotation, all of these messages (layoutSubviews, viewWillLayoutSubviews, and viewDidLayoutSubviews) are sent inside the autorotation's animation block, so if you do your layout in one of these methods, you also get the benefit that the changes to your layout will be animated during the autorotation animation.
First of all, if you don't want to get the view resized when rotating check the autoresize mask. If you built the view in the interface builder then check that it's not changing it's size when the superview does:
You can check the current interface orientation of your UIViewController with self.interfaceOrientation. This might help you if you want to set up you view manually.
Furthermore, if you need to adjust the sizes manually then set the desired frames to your views before (or after) the rotation is performed:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
I would recommend query the status bar orientation to get the current rotation in viewDidLoad I would recommend not relying on auto-resizing for scroll views as it tends to get tricky. Check the status bar the resize proportionally based on that.
[UIApplication sharedApplication].statusBarOrientation

Resources