having issues with UIScrollView. I have a setup like so:
I have a scrollview pinned in the first image, trailing, leading, top and bottom constraints. In the second image I have place a UIView of the same dimensions inside the scroll view (I plan to add content to this). This is pinned to the scroll view and also centred horizontally and vertically. It seems no touches are registered at all when I try to scroll now. I have set a large content size:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print("The scroll views height is \(scrollView.frame.size.height)")
scrollView.contentSize = CGSize(width: scrollView.frame.size.width, height: 1000)
print("The scroll view content height is: \(scrollView.contentSize.height)")
}
I have also enabled user interaction everywhere I can. I am using the delegate method scrollViewDidScroll(scrollView: UIScrollView) to check if touches are being registered and they aren't at all. What am I doing wrong here?
Hope this will help you,Many facing this problem i hope my solution will provide relief to Devs.
your view Hierarchy should be like this :-
View (main view of my UIViewController) – with
-ContainerScrl
--Scroll View (UIScrollView)
---ViewInsideScrl
----Content1
----Content2 (etc)
As, the ScrollView only Scroll When its Content Size will be greater then the frame of Scrollview.
Now Comes the imp. part the constraint Should be like:-
Give constraint to the ContainerScrl and then Scroll View Should be pinned from all the direction to the ContainerScrl and ViewInsideScrl should be pinned to Scroll View. Now it will be giving warning like scrollable content size Ambiguity.
Look, if u give constraint like width and height of ViewInsideScrl should be equal to ContainerScrl, all the constraint error msg will be vanished but it will not scroll as frame getting equal to content size,, let suppose u want it to scroll in horizontal dirction then just give equal height constraint to both the view and give proportional constraint to ViewInsideScrl width constraint w.r.t ContainerScrl like
ViewInsideScrl = 2* ContainerScrl ;
it will make the content of scroll bigger then the frame.
Lets try this, if problem not get solved we will look further to it.
Related
Problem:
I have a UIViewController which contains a ContainerView which further is embedded with a UICollectionViewController. I am trying to create a UICollectionViewCell that contains a UIScrollView. My CollectionViewLayout is Flow and scroll direction is horizontal, which when used without the scrollview, gives a perfect feel and doesn't roam about the area whereas when the cell has scrollview, which is supposed to scroll vertically whereas the cell is horizontally scrollable, it creates a bad user experience and i need the scrollview to just scroll vertically whereas i want the cell to scroll horizontally when required.
This is how my CollectionView is setup:
And my ScrollView:
You are constraining the view inside the scroll view with Leading and Trailing of 20-pts and you are constraining its width equal to the width of the scroll view:
So, if your scroll view's width ends up being 500, it's .contentSize will be 500 + 20 + 20 and will scroll horizontally.
Keep your width constraint equal to the scroll view's width, but give it a Constant of -40. That will stop the horizontal scrolling.
Using Storyboard, in UIViewController using UIScrollView, UIView as content view
Scrollview Constraints - top, bottom, left, right
UIView as contentview constraints - top, bottom, left, right, equal width height to ViewController's View.
I am using these constraints, can anyone please help me out why button is not calling?
The button is not clickable because it is below the frame of the content view. You need to remove all auto layout constraints from your content view (the UIView inside the scrollview).
Then you can add all the objects that you need to add to the content view and set the height of the content view according to the height of the content.
So lets say that you calculated a height of 1000 for the content of the objects in the scroll view. You would then need to set the frame of the content view like this:
contentView.frame = CGRectMake(0,0,scrollView.frame.size.width, 1000);
And don't forget to set the contentSize for the scrollview so that the scrollview knows how much room it needs to scroll.
Just now found the answer with removing any autolayout constraints, for content view we have to set constraints like below:
Top, bottom, left, right
Align CenterX - here we have to set the content view height then for that constraint we have to set constant as scrollview content size height
I'm basically trying to reproduce the behavior of the title and message section of an alert.
The title and message labels appear to be in a scroll view. If the label text increases then the alert height also increases along with the intrinsic content size of the labels. But at a certain height, the alert height stops increasing and the title and message text become scrollable.
What I have read:
Articles
Auto Layout Magic: Content Sizing Priorities
Editing Auto Layout Constraints (documentation)
A Fixed Width Dynamic Height ScrollView in AutoLayout
Using UIScrollView with Auto Layout in iOS
Stack Overflow
Adding priority to layout constraints
Inequality Constraint Ambiguity
UIScrollView Scrollable Content Size Ambiguity
Ambiguity with two inequality constraints
IOS scrollview ambiguous scrollable content height in case of autolayout
The answer may be in there but I was not able to abstract it.
What I have tried:
Only focusing on the scroll view with the two labels I tried to make a minimal example in which a parent view would resize according to the intrinsic height of the scrollview. I've played around with a lot of constraints. Here is one combo (among many) that doesn't work:
I've worked with auto layout and normal constraints and even intrinsic content sizes. Also, I do know how to get a basic scroll view working with auto layout. However, I've never done anything with priorities and content hugging and compression resistance. From the reading I've done, I have a superficial understanding of their meanings, but I am at a loss of how to apply them in this instance. My guess is I need to do something with content hugging and priorities.
I think I have achieved an effect similar to the one you wanted with pure Auto Layout.
THE STRUCTURE
First, let me show you my structure:
Content View is the view that has the white background, Caller View and Bottom View have a fixed height. Bottom View has your button, Caller View has your title.
THE SOLUTION
So, after setting the basic constraints (note that the view inside scroll view has top, left, right and bottom to the scroll view AND an equal width) the problem is that the scroll view doesn't know what size should have.
So here comes what I have done:
I wanted that the scroll could grow until a max. So I added a proportional height to the superview that sets that max:
However, this brings two problems: Scroll View still doesn't know what height should have and now you can resize and the scroll view will pass the size of his content (if the content is smaller than the max size).
So, to solve both issues I have added an equal height with a smaller priority from the View inside of the Scroll View and the Scroll View
I hope this can help you out.
Your problem can't be solved with constraints alone, you have to use some code. That's because the scroll view doesn't have an intrinsic content size.
So, create a subclass of scroll view. Maybe give it a property or a delegate or something to tell it what its maximum height should be.
In the subclass, invalidate the intrinsic content size whenever the content size changes, and calculate the new intrinsic size as the minimum of the content size and the maximum allowed size.
Once the scroll view has an intrinsic size your constraints between it and its super view will actually do something meaningful for your problem.
It can be done in Interface builder using auto layout without any difficulties.
set outer container view ("Parent container for scrollview" in your sample) height constraint with "less than or equal" relation.
2.add "equal heights" constraint to content view and scroll view. Then set low priority for this constraint.
That's all. Now your scrollview will be resized by height if content height changed, but only to max height limited by outer view height constraint.
You should be able to achieve this solution via pure autolayout.
Typically if I want labels to grow as their content grows vertically I do this
[label setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[label setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
In order for your scrollview to comply to your requirements you will need to make sure a line can be drawn connecting the top of the scrollview all the way through the labels to the bottom of the scrollview so it can calculate it's height. In order for the scrollview to confine to it's parent you can set a height constraint with a multiplier of the superview of say 0.8
You can do this fairly simply with two constraints
Constraint 1: ScrollView.height <= MAX_SIZE. Priority = Required
Constraint 2: ScrollView.height = ScrollView.contentSize.height. Priority = DefaultHigh
AutoLayout will 'try' to keep the scrollView to the contentSize, but will 'give up' when it matches the max height and will stop there.
the only tricky part is setting the height for Constraint 2.
When my UIStackView is in a UIViewController, I do that in viewWillLayoutSubviews
If you're subclassing UIScrollView to achieve this, you could do it in updateConstraints
something like
override func viewWillLayoutSubviews() {
scrollViewHeightConstraint?.constant = scrollView.contentSize.height
super.viewWillLayoutSubviews()
}
To my project, I have a similar problem. You can using the following way to make it work around.
First, Title and bottom action height are fixed. Content has variable height. You can add it the mainView as one child using the font-size, then call layoutIfNeeded, then its height can be calculated and saved as XX. Then removed it from mainView.
Second, using normal constraint to layout the content part with scrollView, mainView has a height constraint of XX and setContentCompressionResistancePriority(.defaultLow, for: .vertical).
Finally, alert can show exact size when short content and show limited size when long size with scrolling.
I have been able to achieve this exact behavior with only AutoLayout constraints. Here is a generic demo of how to do it: It can be applied to your view hierarchy as you see fit.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let kTestContentHeight: CGFloat = 1200
// Subview that will shrink to fit content and expand up to 50% of the view controller's height
let modalView = UIView()
modalView.backgroundColor = UIColor.gray
// Scroll view that will facilitate scrolling if the content > 50% of view controller's height
let scrollView = UIScrollView()
scrollView.backgroundColor = .yellow
// Content which has an intrinsic height
let contentView = UIView()
contentView.backgroundColor = .green
// add modal view
view.addSubview(modalView)
modalView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([modalView.leftAnchor.constraint(equalTo: view.leftAnchor),
modalView.rightAnchor.constraint(equalTo: view.rightAnchor),
modalView.heightAnchor.constraint(lessThanOrEqualTo: view.heightAnchor,
multiplier: 0.5),
modalView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])
let expandHeight = modalView.heightAnchor.constraint(equalTo: view.heightAnchor)
expandHeight.priority = UILayoutPriority.defaultLow
expandHeight.isActive = true
// add scrollview to modal view
modalView.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([scrollView.topAnchor.constraint(equalTo: modalView.topAnchor),
scrollView.leftAnchor.constraint(equalTo: modalView.leftAnchor),
scrollView.rightAnchor.constraint(equalTo: modalView.rightAnchor),
scrollView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)])
// add content to scrollview
scrollView.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
contentView.widthAnchor.constraint(equalTo: modalView.widthAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.heightAnchor.constraint(equalToConstant: kTestContentHeight)])
let contentBottom = contentView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)
contentBottom.priority = .defaultLow
contentBottom.isActive = true
}
}
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
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);