How to make this auto layout arrangement work with scrollview - ios

I have a screen layout where there are two resizable labels , which will contain multiline text. These labels are placed inside their parent views which intern are added to main contentView, main contentView is then added to scrollView ( thats what most of the solutions suggests). For both the labels (below About and Time and location labels in first image attached) I have set height constraints as "greater than or equal to" and setting the numberOfLines to 0 as well as calling SizetoFit, but actual output is not as expected (see second image attached). There are no constraints warnings. All constraints are provided for all the elements.
The code in viewDidLoad is as follows for one of the label.
self.lblAbout.text = #"this is a long two three lines about string which will have two lines this is a long two three lines about string which will have two lines";
self.lblAbout.numberOfLines = 0;
[self.lblAbout sizeToFit];
[self.lblAbout setPreferredMaxLayoutWidth:244.0];
Also
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
scrollView.contentSize = contentView.frame.size;
}
Not if any additional constraints are needed, I have added all leading , trailing , top , bottom constrains along with height wherever needed, plus the spacing between all the views is in place.
What i want is the labels should get adjusted to number of lines and the contentView (parent view) should scroll inside ScrollView as the total height will be larger than the screen available.
*** problem I think is the outer view of the labels aren't getting resized as per the label because of which all the views below it aren't getting repositioned ****

Please try this Solution,
1. add Height Constraint to superView of your Label.
2. add IBOutlet of that Constraint
3. add this Method to find out Height of your Text
you need to give width of super view of your Label so width will be same as your super view
4. Now get Height form returned CGRect and assign it to your Constraints's constant. it should be like
heightConstraint.constant = youObject.size.height;
please Make sure you have added other Constraints accordingly this. if not than you need to also increase Height of other superviews accordingly.
(CGRect)sizeOfDetailLabelFromString:(NSString*)string maxWidth:(CGFloat)maxWidth{
NSDictionary *attributes = #{NSFontAttributeName:FONT_LIGHT};
CGRect rect = [string boundingRectWithSize:CGSizeMake(maxWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:attributes context:nil];
return rect;
}

Here is what worked finally.
Added ScrollView in main View
Added View as a contentView ( be sure to rename it in designer to something than just view )
pinned scrollview to main view using leading trailing top and bottom space constraints.
Pinned contentView to scrollview same as above
Added all the components with their respective constraints
Set the height of UILabel which i want to resize using "Greater than or equal to constraint (this is necessary) and set number of lines to 0 in code
The parent view of label shouldn't have any fixed height but enough constraints to calculate it at runtime.
Make sure ScrollView has no ambiguity in calculating contentSize.
imp - Add constraints to width of Main view , scrollview , contentView ( that was in my case , you may not need equal width constraint between contentView and scrollview , but between contentView and main view its necessary)
Now scrollview scrolls exactly the way it's needed.
To my belief most of the above things i already did but somehow it wasn't working ,deleted everything and did all the things again few times and it worked.

Related

Make UIScrollView with dynamic height subviews work automatically with Auto Layout

I have the following view structure with its constraints:
UIView "parent"
UIScrollView (leading, trailing, top and bottom to superview)
- UIView "container" (leading, trailing, top and bottom to UIScrollView,
equal width and height so parent UIView)
- UIView "A" (leading, trailing, top to UIScrollView, height of 200)
- UIView "B" (top, leading and trailing to UIView A, height of 140)
- UIView "C" (top, leading and trailing to UIView B, height >= 88 "rest
of the screen until bottom", bottom to UIView "container")
"A" and "B" UIView's do not change its size but "C" does. Inside it, I am adding programmatically n "labels containers" UIView that have different heights depending on the content of m UILabel that they host.
Right now, I am calculating the size of the n UILabel with boundingRectWithSize: and I am sizing the height of their parent "labels containers" UIView that it is being added inside "C" UIView setting its height constraint to the sum of all UILabel.
Then, I resize "C" UIView height constraint so that it is equal to the sum of all added UIView.
This is working perfectly on all different screen sizes, portrait and landscape. The UIScrollView is showing all three "A", "B" and "C" subviews, having "C" n UIView that host m UILabel.
But now I am having troubles when rotating the device. I face the problem that I have to recalculate the size of all UILabel to change the height constraint of all the "labels container" UIView and change the "C" UIView height constraint so that it can fit everything without large blank spaces between all views.
So my question is: how can I achieve the same behaviour using exclusively Auto Layout?
Now I have to recalculate sizes and change height constraints so that everything adapts, but I would love that all UILabel resize themselves automatically and fit their content, then the "labels container" UIView resize to fit all the UILabel and then "C" UIView resizes automatically to fit the content.
Thank you in advance!
As per my knowledge you need to add TableView for your dynamic labels and to achieve this you have to follow below steps:
1step : Add ScrollView in your ViewController.
2step : Add ContainerView in ScrollView to manage scroll constrains.
3step : Add your "A" view with fixed size lets say 150px.
4step : Add your "B" view with fixed size lets say 150px.
5step : Add TableView for dynamic labels with fixed size 0px. At this step make Outlet of your tableView height Constrains we will use it in next step.
6step : Call your service and add labels in your tableView.
in this 6th step while you are adding labels in your table view means while you reload your tableView do below code for dynamic heigh constrains.
7step : Now as per your Row height adjust height of table view as mention below.
arrListArray = #[#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Ten"];
int rowHeight = 44;
self.tblHeightConstraint.constant = (rowHeight * arrListArray.count);
now reload your constrains with below method:
[UIView animateWithDuration:0.1
animations:^{
// Called on parent view
[self.view layoutIfNeeded];
}];
it will increase your scrollView contain size automatically.
Show sample images :
View Constrains Stack :
Now as per your comment you have to add CollectionView in place of tableView and add TableView in CollectionViewCell with your specific design.
Hope this will helps!
Ok, I was doing three things wrong:
1st: I was calculating the height of the labels to set then the size of the "labels container" UIView using a height constraint. Height constraints not needed as Auto Layout manages everything.
2nd: I was modifying the "C" UIView constraint height manually by adding the height value of each "labels container" UIView added.
3rd: After fixing the first two steps, I forgot to set the last added UIView constraint with its parent view...
Now everything works as expected.

set dynamic equal height of two uiview depends on their subview using autolayout?

I have one superview in that view there are two subview.
I have set equal height of that two view. Those subview height changing respect to label height in it.
This is first screen that i have implemented. It works when label 2 height is greater then label 1 height changes with respect to label 2 height.
This is first screen. It work properly but another situation it is not working when label 1 height is greater than label 2 height then it's not working expected.
output like following screen
As expected label 2 need to adjust with respect label 1 but then also label 1 is adjusted with respect to label 2.
so how to apply constraint for set equal height for two view depends on their subview height.
You can make your label heigh dynamically
Call a method for UILabel height
-(CGSize) getContentSize_Label:(UILabel*) myLabelView{
return [myLabelView sizeThatFits:CGSizeMake(myLabelView.frame.size.width, FLT_MAX)];
}
and set label heigh like
myLabelView.frame = CGRectMake(myLabelView.frame.origin.x, myLabelView.frame.origin.y, myLabelView.frame.size.width, [self getContentSize_Label:myLabelView].height);
and set
myLabelView.numberOfLines = 0;
What you are missing is most probably equal height constraint.
Select two container views of the labels and then go to Xcode/Editor/Pin/ and select Height equally.
I've just checked it and its working for me.

UIScrollView autolayout - vertical scroll only

UIScrollView is a little special in the way it interprets auto layout constraints, as geometry of the subviews fills its contentSize as opposed to frame/bounds. Apple has a nice write up on the matter here: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
The problem I'm facing is when I try in place multiline labels into the scrollView.
An example constrain looks like this:
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[image(==imageWidth)]-15-[captionLabel]|", options: .AlignAllCenterY, metrics: metrics, views: views))
I'm not showing additional subviews that are handled by other scrollView.addConstraints() calls which take up more vertical space.
Ideally, I want both elements to fit horizontally within the screen, and have the captionLabel break into multiple lines as necessary. Instead, the whole text is rendered into a single line and the scrollView sets its contentSize to accommodate the larger width.
What options are there to solve with auto layout?
Edit: Not too happy about nesting more contentViews, so I ended up setting the desired width of the label in a separate constraint, and updating it in viewWillTransitionToSize. In my case, a whole list of similar "rows" of labels in used, and only the first one has width set through a calculated constraint, the rest are set to be equal to the first.
First your view hierarchy should looks like this:
the most important is that you have only one child in your scroll view it will act as a content view.
The constraints :
ScrollView = 4 constraints from edges to the main view with 0 spacing
ContentView = 4 constraints from edges to the ScrollView with 0 spacing
And to avoid horizontal scrolling
add a constraint of equal width between ContentView and Main view (not ScrollView)
now add content to your content view. Use Auto Layout, like you normally would. Don't forget to set 0 as number of lines for your label add the top, right, left bottom constraints of the label.
The result should be like mine :
More information: Using UIScrollView with Auto Layout in iOS
I don't know if this can help you (i hope understand good the question), but if you only want scroll Vertical, you can add this in your implementation:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Vertical Only ** If you have several scrolling objects detect by Tag
if (scrollView.tag == 100) {
CGPoint offset = scrollView.contentOffset;
offset.x = 0;
scrollView.contentOffset = offset;
}
}
If you want a storyboard solution, you can set the constraint "Equal Widths" twice: between your UIScrollView and its Superview, and between your UILabel and your UIScrollView

UIScrollView does not Scroll completely

I have a ViewController. In it I put ScrollView with the View(contentView). Later I drag from contentView to View and set Equal Height. Now it scrolls, but not fully.
As you see there are it has continue below the textView, but it
doesn't scrolls. How can I fix it?
UIScrollView is able to automatically calculate it's content height and width, but you need to help it with this.
To do so you need to:
Bound contentView (in your case) to all sides of superview (which is Scroll View).
Let contentView to calculate it's sizes. Here is a small mistake in your approach. You've set height of the contentView equal to View's height. So basically Scroll View's contentSize.height is the same as View's height. Which is not really what you want with dynamic content.
Usually you want to set width of the contentView equal to View's width and do not set contentView's height. Instead you want to bind subviews of contentView to their superview in such a way that their superview (contentView) will calculate it's height automatically.
In your case I would bind:
pizza.jpg to left-top-right of superview (height of pizza.jpg will be set from intrinsic image size);
SAMPLE TITLE label - left-right to superview; top to pizza.jpg image;
Text View - left-bottom-right to superview; top to SAMPLE TITLE label; set a fixed height.
In this case contentView will define needed height by itself. Scroll View will set it's contentSize accordingly.
And your screen will be able to scroll vertically (it should be) ;)
You need to set the contentsize of the scrollview. Use the below code to do that:
func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.contentView.frame.size.height);
}
To use Autolayout with UIScrollView is tricky.
In your code you have to update height constraint for your contentView by calculating height of subviews of contentView and that will automatically update the contentSize for your ScrollView and you can scroll through all subviews.
For more info to use Autolayout+UIScrollView your can read this.
According to this link (thanks to this Matt's answer first), UIScrollView acts differently with AutoLayout than the other views.
Subviews of a scrollView set their constraints from the contentView of the scrollView and not the scrollview directly. This allows the content to scroll.
So :
Add a UIView to your scrollView, this will represent the contentView of your scrollView. Add constraints to top, bottom, trailing, leading from the view to its superView
Interface Builder complains. Here you see the different between a basic view and a scrollView. The reason is a contentView has to be fill to know its size. So add a equal width from the contentView to the scrollView
The contentView knows now its width but not its height. So add your labels and your UIImage as subviews of the contentView. Add constraints from bottom to the top. Don't miss to add a height constraint to the UIImageView.
It should look like this :
Hope this helps
Read this (from Matt once again) for further informations

How to make uiscrollview only vertical scrolling for ios?

I'm trying to make layout inside scrollview using this one tutorial link
And get the following result link
It will be appreciated for any advices or tutorial links. It needs only vertical scrolling
I am sure there must be other ways to do this but a quick fix is :
1.) Create a width constraint on ContentView in Storyborad.
2.) IBOutlet that widthContraint and set its value to the view frame width in viewDidLoad.
Suppose the name of the constraint outlet is contentViewWidthContraint.
contentViewWidthContraint.constant = self.view.bounds.size.width;
Another alternative to do so from Storyboard, is to fix the Contentview width to the view's width from the storyboard or to the Scrollview, if Scrollview already has a Equal width contraint with superview . Add the "Equal Width" contraint from Contentview to either self.view or to Scrollview (if scrollview, already has the width contraint)
Have you set up the "ContentView" width to match with the scroll view width? I had the same problem and I fixed with "Equal Widths".
"Equal Widths" will tell to your "ContentView" to use the same width of the "Scroll View", which should be fitting the screen if you have set up the constrain properly.
You can do this easily on the storyboard.
Drag and drop, with right click (important!!!), from "ContentView" to "ScrollView"
Release the click, you will be prompted with a menu, select "Equal Widths".
This should fix your problem using the scrollview with AutoLayout from Storyboard editor.
You can find a full tutorial how to use ScrollView with Autolayout and Storyboard here.
I hope this is useful for you :)
In the Storyboard set the width of the elements contained in your UIScrollView equal to the width of this UIScrollView (by selecting all elements and the UIScrollView holding in the panel on the left of your Storyboard and then setting the 'Equal Widths' constraint under 'Pin' on the bottom of your Storyboard). Just pinning the right sides of the elements to that of the UIScrollView won't work as it will adjust the size of its "display view" to the width of the largest element and if this is smaller than the width of the UIScrollView all elements will just appear aligned to its left side.
There is also another possibility that offers a very good result.
You can mark a checkbox:
O programmatically:
scrollView.alwaysBounceVertical = true
Try to set it's width to 0 & height equal to content size like this:
self.scrollView.contentSize = CGSizeMake(0, self.scrollView.contentSize.height);
This will work as you want. Try it & tell if still facing any issue.
For disabling the horizontal scroll, you can set the content size in the -(void)scrollViewDidScroll method.
[self.scrollView setContentOffset: CGPointMake(0, self.scrollView.contentOffset.y)];
self.scrollView.directionalLockEnabled = YES;
This is because scroll view have no idea where your content should end.
But when at least one item inside your scroll view has its "trailing space" constraint attached to a view outside the scroll view (usually a view the scroll view is sitting in or some other view of a higher level, which "knows" its width) - the scroll view will automatically get an idea about your wanted width and won't scroll horizontally (unless that trailing constraint implies having your content outside the screen).
Better if all items inside scroll view have their "trailing space" constraints connected either to each other or to a view outside the scroll view. But not the scroll view itself.
No additional code or extra constraints needed for this to work.
Too set UIScrollView constraints as like below code so it will occupied whole screen.Not exceed the screen size.
Leading Space = 0 from mainView
Top Space = 0 from mainView
Bottom Space = 0 from mainView
Trailing Space = 0 from mainView
You need to set the width of UIScrollView equal to or less than the width of your Parent View. Two ways to do it:
1) You can do this in Storyboard via layout constraints
2) You can do this programatically:
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.scrollView.contentSize.height);

Resources