Define correctly height for UIScrollView - ios

I'm creating a application using Swift3, and i have difficult to define correctly height for UIScrollView, i'm using autolayout and create this structure:
UIScrollView
UIView // The container view
UIImageView // Constraint Top Edges = 20 in relation to UIView
UITextView // Constraint Top Edges = 40 in relation to UIImageView
UITextView // Constraint Top Edges = 20 in relation to UITextView
UIButton // Constraint Top Edges 30 in relation to UITextView
Currently, i'm using this logic to calculate UIScrollView height
override func viewDidLayoutSubviews() {
var scrollHeight : CGFloat = 0.0
for view in self.containerView.subviews {
view.layoutIfNeeded()
scrollHeight += view.frame.size.height
}
// Adding height for scrollview, according to the sum of all child views
self.scrollView.contentSize.height = scrollHeight
}
But, i can only get the views height, and them not consider the Constraints "margins", i would like know any way to calculate correct height for UIScrollView, adjusted according their content.

Close! Let's add the top margin for each view:
override func viewDidLayoutSubviews() {
var scrollHeight : CGFloat = 0.0
for view in self.containerView.subviews {
view.layoutIfNeeded()
scrollHeight += view.frame.size.height
//Add the margins as well to the scrollHeight
scrollHeight += view.layoutMargins.top
}
// Adding height for scrollview, according to the sum of all child views
self.scrollView.contentSize = CGRect(x: self.scrollView.contentSize.width, y: scrollHeight)
}

self.scrollview.contentsize.height = containerview.height;

Related

Autoresizing UITableViewCell with UITextView which has text aligned to vertical center simultaneously?

Cell contains multiline text so I decided to use text view.
textView.textContainerInset = UIEdgeInsets.zero removes extra padding. Code to center it vertically:
extension UITextView {
func centerVertically() {
var topCorrect = (self.bounds.size.height - self.contentSize.height * self.zoomScale) / 2
topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect;
self.contentInset.top = topCorrect
}
}
This works if I predefine cell height in tableView(_:, heightForRowAt:).
But if I use UITableView.automaticDimension to resize the cell everything becomes broken.
How to solve this issue?
Use auto layout and pin leading, trailing, top and bottom, constraints of textview to the cell.
Disable scrolling in textview
Add a height constraint such that height is greater than or equal to a constant value (this is a min value that will show on the screen) for textview
User .automatic dimension
Try this and let me know if this worked out for you

How do I create, add and position a UIScrollView programmatically so that it fills the screen?

I want to create a simple view hierarchy in a Swift project that has a UIScrollView which should act as an expandable container to the subviews which are: UILabel, UITextView and a Button.
The catch here is that I am doing it all programmatically using visual format language and cannot do it in Interface Builder. My code below displays a scrollview, however it cannot scroll down to show the views which are below it and the views themselves are not sized correctly. It is static. Also the subviews are not expanding to fill up the screen.
I want to display a scrollview which is fullscreen size and also its subviews fill the screen horizontally with varying heights depending on what size I set. They currently only are around 200pt wide which is unusual.
viewDidLoad() {
view.addSubview(scrollView)
//This function is a convenience function which applies constraints
view.addConstraints(withFormat: "H:|[v0]|", toViews: scrollView)
view.addConstraints(withFormat: "V:|[v0]|", toViews: scrollView)
//Here I add the 3 subviews mentioned above
scrollView.addSubview(nativeText)
scrollView.addSubview(mnemonicDescription)
scrollView.addSubview(addButton)
//Here I apply constraints using format language
scrollView.addConstraints(withFormat: "H:|[v0]|", toViews: foreignText)
// Note that this should make foreignText expand the full width. It doesn't, its very small
//I continue to add subviews with horizontal and vertical constraints however they do not fill the container view as expected.
}
Here is a (fairly) simple example of adding 3 views to a Scroll View and using VFL to set the constraints.
override func viewDidLoad() {
super.viewDidLoad()
// create a scroll view with gray background (so we can see it)
let theScrollView = UIScrollView()
theScrollView.backgroundColor = .gray
// add it to the view
view.addSubview(theScrollView)
// add constraints so the scroll view fills the view
view.addConstraintsWithFormat("H:|[v0]|", views: theScrollView)
view.addConstraintsWithFormat("V:|[v0]|", views: theScrollView)
// create three UIViews - nativeText (red), mnemonicDescription (green), addButton (blue)
let nativeText = UIView()
nativeText.translatesAutoresizingMaskIntoConstraints = false
nativeText.backgroundColor = .red
let mnemonicDescription = UIView()
mnemonicDescription.translatesAutoresizingMaskIntoConstraints = false
mnemonicDescription.backgroundColor = .green
let addButton = UIView()
addButton.translatesAutoresizingMaskIntoConstraints = false
addButton.backgroundColor = .blue
// add those three views to the scroll view
theScrollView.addSubview(nativeText)
theScrollView.addSubview(mnemonicDescription)
theScrollView.addSubview(addButton)
// set horizontal / width constraints for the three views so they fill the scroll view
// "H:|[v0(==v1)]|" means "make the width of v0 equal to the width of v1, and pin to leading and trailing"
theScrollView.addConstraintsWithFormat("H:|[v0(==v1)]|", views: nativeText, theScrollView)
theScrollView.addConstraintsWithFormat("H:|[v0(==v1)]|", views: mnemonicDescription, theScrollView)
theScrollView.addConstraintsWithFormat("H:|[v0(==v1)]|", views: addButton, theScrollView)
// set the vertical / height constraints of the three views
// (==200) means "set the height to 200"
// "|" means "pin to edge"
// "-40-" means 40 points of space
// so the following 3 lines will put:
// nativeText (Red view) pinned to the top of scrollview, height of 200
theScrollView.addConstraintsWithFormat("V:|[v0(==200)]", views: nativeText)
// mnemonicDescription (Green view) pinned 40 space to Red view, height of 300
theScrollView.addConstraintsWithFormat("V:[v0]-40-[v1(==300)]", views: nativeText, mnemonicDescription)
// addButton (Blue view) pinned 40 space to Green view, height of 250, *and* pinned to bottom of scrollview
theScrollView.addConstraintsWithFormat("V:[v0]-40-[v1(==250)]|", views: mnemonicDescription, addButton)
// it could also be expressed in a single statement
// comment out the above three lines of code, and
// un-comment this line to see the same result
//theScrollView.addConstraintsWithFormat("V:|[v0(==200)]-40-[v1(==300)]-40-[v2(==250)]|", views: nativeText, mnemonicDescription, addButton)
// using those example heights and spacing comes to a total of 830,
// so it will scroll vertically a little bit on a iPhone 7+ (736 pts tall)
}
This is not a very difficult thing to achieve. If you will research carefully you can find the answers to all you queries here itself.
Anyways I am providing you with the detailed code for the thing you want to achieve. Hope this help you.
Simply add this code below your ViewController Class.
let scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
// Variables for setting the scrollView and Views inside scrollView
let phoneHeight: CGFloat = self.view.frame.size.height
let phoneWidth: CGFloat = self.view.frame.size.width
var yPosition: CGFloat = 0
var scrollViewHeight: CGFloat = 0
// Setting scrollView attributes
scrollView.frame.size.height = phoneHeight
scrollView.frame.size.width = phoneWidth
scrollView.frame.origin.x = 0
scrollView.frame.origin.y = 0
// Adding scrollView to the mainView
self.view.addSubview(scrollView)
// Creating subViews for the scrollView
let view1 = UIView()
let view2 = UIView()
let view3 = UIView()
let view4 = UIView()
let view5 = UIView()
// Adding the subVies in an array
var subViewArray: [UIView] = []
subViewArray.append(view1)
subViewArray.append(view2)
subViewArray.append(view3)
subViewArray.append(view4)
subViewArray.append(view5)
// Adding the subViews to the scrollView
for subView in subViewArray {
// Setting the atributes for subViews
// Fixed height of 200px and width adjusts according to the phoneSize
subView.frame.size.height = 200
subView.frame.size.width = phoneWidth
subView.frame.origin.x = 0
subView.frame.origin.y = yPosition
// Adding the background color to the subViews
subView.backgroundColor = UIColor.blue
// Changing the yPosition and scrollViewHeight and adding a 20px margin for each subview in scrollView
yPosition += 200 + 20
scrollViewHeight += 200 + 20
// Adding labels to the subviews
let label1 = UILabel()
label1.text = "Label Added"
label1.textAlignment = .center
label1.textColor = UIColor.white
label1.frame.size.height = 30
label1.frame.size.width = phoneWidth
label1.frame.origin.x = 0
label1.frame.origin.y = 10
subView.addSubview(label1)
self.scrollView.addSubview(subView)
scrollView.contentSize = CGSize(width: phoneWidth, height: scrollViewHeight)
}
}

Disabling Horizontal Scrolling from UIScrollView Swift

Scroll View
I have a UIScrollView, with constraints left: 0, top: 0, right: 0, bottom: 0
Inside Scroll View
At the top of this UIScrollView is a UIImageView with constraints left: 0, top: 0, right: 0, height: 200
Underneath this I have a UITextView with constraints left: 0, top: 0, right: 0, bottom: 0
This means the UITextView will resize with respect to its content, and I set the scrollingEnabled to false for the UITextView.
So, when I run, it almost works perfectly.
The one problem is the UIImageView takes up about 10% more than the actual screen width. Hence, horizontal scrolling is enabled.
I have tried adding the lines
imageView.frame = CGRect(0, 0, screenSize.width, 200)
scrlView.contentSize.width = screenSize.width
but this makes no difference. I can still scroll horizontally and the Image View still takes up around 10% more than the actual screen width.
Note, I have not set imageView screen width in storyboard, only programatically.
Any ideas?
Like this,
Swift 4.0
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.x>0 {
scrollView.contentOffset.x = 0
}
}
And, you can set this property:
scrollImg.isDirectionalLockEnabled = true
Swift 4
Horizontal Scroll Lock
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x != 0 {
scrollView.contentOffset.x = 0
}
}
You can change the x to y for vertical scrolling.
Make sure to add UIScrollViewDelegate like this:
class MyViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
...
}
And set the delegate for the ScrollView
scrollView.delegate = self
I changed this so that it just returns 0. No need to check at all if you want scroll off.
func scrollViewDidScroll(scrollView: UIScrollView) {
scrollView.contentOffset.x = 0
}
No need for the directional lock.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentOffset.x = 0
}
This stops the scrollview from scrolling towards the leading edge too.
If you are using storyboard and autolayout, then you should consider how ScrollView work in Storyboard with autolayout.
Consider the following.
Add a single view on your scrollView with constrains
left, right, top, bottom, height, width,
Make the outlet of the width and height
Add your subViews to the view you added in the scrollView
update the width to the screenSize eg: 320 for iPhone5 or 4.
self.viewWidth = SCREEN_WIDTH;
[self.view updateConstraints];
You can use
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.contentOffset.x = 0
}
Do not use scrollView.contentOffset.x < 0 or > 0 like others have mentioned because it will only stop scroll to one side horizontally and not both.

Make Top image in UIScrollView enlarge on scroll view bounds?

I have a UIScroll view with a UIView inside as content view. The layout is:
self.scrollView = UIScrollView()
self.view.addSubview(self.scrollView)
self.scrollView.backgroundColor = UIColor.yellowColor()
// pin all edges to the edges of the superview (self.view)
self.scrollView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.view)
}
// create contentView
self.contentView = UIView()
self.contentView.backgroundColor = UIColor.redColor()
self.scrollView.addSubview(self.contentView)
// pin the edges of the contentView to the scrollView
self.contentView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(self.scrollView)
}
In my contentView I have a UIImageView at the top and some UILabels below:
let imageView = UIImageView(...)
let labe1 = UILabel(...)
let labe2 = UILabel(...)
self.contentView.addSubView(imageView)
self.contentView.addSubView(labe1)
self.contentView.addSubView(labe2)
Here is how it looks like:
When I pull down the scroll view the scroll view bounces at the top. I want that the image enlarges when the scroll view bounces.
I did this with the UIScrollViewDelegate:
func scrollViewDidScroll(scrollView: UIScrollView) {
let offsetY: CGFloat = scrollView.contentOffset.y
if offsetY < -64 {
let progress:CGFloat = fabs(offsetY + 64) / 100
self.imageView.transform = CGAffineTransformMakeScale(1 + progress, 1 + progress)
}
}
This works but the problem is that when the image is transformed it should not overlay the border to the red area which contains the Labels. Here is what happens when I bounce:
I want that the image still enlarges but not overlapping the border to the red area. How can I do that?
I suggest you to resize the whole UIScrollView with pager view - don't forget to reset its's frame back to prevent wrong paging.
Just add an empty UIView with some background color as highlighted in image
Or in your code
let imageView = UIImageView(...)
let labe1 = UILabel(...)
let labe2 = UILabel(...)
self.contentView.addSubView(imageView)
//add one more UIView with some background color here
self.contentView.addSubView(labe1)
self.contentView.addSubView(labe2)

UIScrollView Content Size with Autolayout

I am using UIScrollView with Auto-layout to change contentSenter code hereize value as following :
UIView
ScrollView
UIView (contentView)
In run time I am adding UITextView & UIImageView to conentView with constraint(Top,Bottom,Left,Right)
but the contentView size is not changing and UIScrollView contentSize also.
So what could be the problem ?
In run time I am adding UITextView & UIImageView to conentView with constraint(Top,Bottom,Left,Right)
Maybe you should add constraint (Top, Left, Height, Width),or (Top, Left, Right, Height)
If you persist in (Top,Bottom,Left,Right), ScrollView will keep the old content size, and make your UITextView & UIImageView (Height = 0, Width = 0).
Here's a neat way of updating scroll view's content size. It's Swift so you'll need a bridging header if you're working in Objective C.
extension UIScrollView {
func updateContentViewSize() {
var newHeight: CGFloat = 0
for view in subviews {
let ref = view.frame.origin.y + view.frame.height
if ref > newHeight {
newHeight = ref
}
}
let oldSize = contentSize
let newSize = CGSize(width: oldSize.width, height: newHeight + 20)
contentSize = newSize
}
}
Just call it on your scroll view's object whenever you add a child view to your scroll view.

Resources