Scroll a UIView in UIScrollView - ios

A lot of questions are available with this but I'm very bad with constraints. So I've added a UIScrollView and the UIView I want to show has height of 700 and this is fixed 700 no dynamic height. The constraints I've for UIScrollView are:
And for UIView the constraints are:
But it's not scrolling.

What I do when I need a scrollable view is what follows - just go over your constraints in storyboards and do the same there (especially pay attention to second step):
I add a scrollView to the hierarchy and use autolayout to properly layout it, e.g., if it is supposed to cover the whole view of the viewController:
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scrollView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
scrollView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
])
Then you need to add a contentView to the scrollView and provide a proper layout constraints for it, so if you want vertically scrollable scrollView in the example I started above, you need following autolayout constraints:
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// horizontal anchors of contentView are constrained to scrollView superview
// to prevent it from scrolling horizontally
contentView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
contentView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
// but vertical anchors of contentView are constrained to
// scrollView to allow scrolling
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
])
Notice here that I constrained the leftAnchor and rightAnchor of the contentView to the self.view rather than to scrollView to make it of fixed width. However, top and bottom anchors are constrained to the scrollView, so they are expanded and scrollable when contentView needs more space.
Now you add to the contentView all the content that you want, and you lay it out using autolayout as if the contentView was a view with infinite height. Or you can just explicitly set its height to 700 as you want.

When you are giving AutoLayout to a scrollView, follow the below methods.
Treat ScrollView like any other view object and apply the constraints like you normally do:
Get a view inside the scrollView which would later contain all the views you would want inside the ScrollView. Apply the constraints like you would apply to a subview, like below:
Apart from the leading, trailing,top and bottom constraints, the width and height are additionally specified.
The width and height would define how much the ScrollView can scroll in the horizontal or vertical direction.
Instead of directly specifying the width and height, you might want to specify the height and width in relation with the other contents you might add inside this subview.
Tip : If you can draw a straight line from top to bottom, connecting the constraints of the Y axis constraints of the subviews, you will not get the ambiguos content error. Same is the case for width.
Programatically, you can follow the same approach:
let scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
let safeArea = view.safeAreaLayoutGuide
scrollView.topAnchor.constraint(equalTo: safeArea.topAnchor, constant: 0).isActive = true
scrollView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
scrollView.backgroundColor = .gray
let scrollContentView = UIView()
scrollView.addSubview(scrollContentView)
scrollContentView.translatesAutoresizingMaskIntoConstraints = false
scrollContentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
scrollContentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
scrollContentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
scrollContentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
scrollContentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
scrollContentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor).isActive = true
scrollContentView.backgroundColor = .green
You can increase the heightAnchor or widthAnchor of the scrollContentView according to your requirement.

Give the height to the contentView inside the scrollview not only to the scrollview itself

Related

How to set ScrollView height with different height constrains and self-sizing labels?

I'm building a scrollView page and it has height constraint ( for example 1300 )
But there is labels need to expand each other ( line height = 0 )
How can i manage scrollView heightConstraint efficient way ?
In order to make scrollView Self sizing you have to set this constraints which pins it to borders of view and doesn't sets certain height for it but instead it uses height of a content inside of scrollView.
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
You can also pin a stack view to the scroll view (or to the layout margins of the scroll view) which will allow the resizing according to the content:
let stackView = UIStackView()
scrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor)
])
Just make sure to provide the intrinsicContentSize for the arranged subviews or at least provide the height for each if you're using .fill, equalSpacing, or equalCentering for the distribution.

How can I change height of the view which is present in StackView

I have a custom UIView using for Custom UITabBar whose height is fixed 50 and I am adding a stackview for each button but I need middle button height should be more.
let view: UIView = views[3]
view.heightAnchor.equalToConstant(100.0).isActive = true
let stackView: UIStackView = UIStackView(arrangedSubviews: [views])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fillEqually
addSubview(stackView)
stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
How can I change the height of a particular view inside the stackView?
I may not understand your problem but you can change anyviews height using constraints as you have already done.
It is same when the view is subview of a stackview. Stackview updates it's size to satisfy its constraint.
You have to have a reference to height constraint of the view inside stackview.
Then you can change it whenever need to.
Also it would be nice to set stackviews alignment property to have desired look.
let heightConstraint = view.heightAnchor.equalToConstant(100.0)
heightConstraint.isActive = true
.
.
.
heightConstraint.constant = 150
You want to change the Alignment property of the stack view...
Assuming 5 views, each with a height constraint of 60, and you want the 4th view (views[3]) to have a height of 100.
StackView Alignment Top:
StackView Alignment Center:
StackView Alignment Bottom:

Adding Stackview to UIScrollView

I have a UIScrollView. It has a stack view. And this stack view contains 12 buttons. (Horizontal scroll view)
Stackview constraints :- top,leading,trailing,bottom to the scroll view and equal widths to the scroll view.
My problem is every time when I run, stack view width limits to the scroll view width and buttons are too small acording to the width of the stack view and my scroll view is not scrollable.
How to make this scrollable
Step-by-Step for setting this up in IB / Storyboards...
Add a view - height 50 leading/top/trailing - blue background
add a scrollview to that view - pin leading/top/trailing/bottom to 0 - set scrollview background to yellow so we can see where it is
add a button to the scroll view
duplicate it so you have 12 buttons
group them into a stack view, and set the stack view's constraints to 0 leading/top/trailing/bottom
and set the stack view's distribution to "equal spacing"
result running in simulator (with no code at all):
and the buttons scroll left and right... no code setting of .contentSize...
So you want this:
Here's how I did it in Xcode 8.3.3.
New Project > iOS > Single View Application.
Open Main.storyboard.
Drag a scroll view into the scene.
Pin top, leading, and trailing of the scroll view to 0. Set height to 30.
Drag a horizontal stack view into the scroll view.
Pin all four edges of the stack view to 0.
Set stack view spacing to 4.
Drag twelve buttons into the stack view.
Set target device to iPhone SE.
Build & run.
Resulting document outline:
If you make your Stackview width equal to the scrollview width, then that's all you'll get, and of course it won't scroll.
Don't give your Stackview a width constraint... let the buttons "fill it out".
Edit: Here is a simple example that you can run directly in a Playground page:
import UIKit
import PlaygroundSupport
class TestViewController : UIViewController {
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
return v
}()
let stackView : UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .horizontal
v.distribution = .equalSpacing
v.spacing = 10.0
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// add the scroll view to self.view
self.view.addSubview(scrollView)
// constrain the scroll view to 8-pts on each side
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 8.0).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -8.0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8.0).isActive = true
// add the stack view to the scroll view
scrollView.addSubview(stackView)
// constrain the stackview view to 8-pts on each side
// this *also* controls the .contentSize of the scrollview
stackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 8.0).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 8.0).isActive = true
stackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -8.0).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -8.0).isActive = true
// add ten buttons to the stack view
for i in 1...10 {
let b = UIButton()
b.translatesAutoresizingMaskIntoConstraints = false
b.setTitle("Button \(i)", for: .normal)
b.backgroundColor = .blue
stackView.addArrangedSubview(b)
}
}
}
let vc = TestViewController()
vc.view.backgroundColor = .yellow
PlaygroundPage.current.liveView = vc

How to Make a UIButton fix at UIScrollView?

I am having an UIButton at my UIScrollView and when i zoom my scroll view the button position is not fix...is there any way to make it fix at one location?
Place/set your button over scroll view (not inside scroll view) as shown here in this snapshot. And also set button constraints (position) with respect to super view of your scrollview.
Here is ref. snapshot of hierarchy of position of each view over each-other.
Since iOS 11, UIScrollView has an instance property called frameLayoutGuide. frameLayoutGuide has the following declaration:
var frameLayoutGuide: UILayoutGuide { get }
The layout guide based on the untransformed frame rectangle of the scroll view.
Use this layout guide when you want to create Auto Layout constraints that explicitly involve the frame rectangle of the scroll view itself, as opposed to its content rectangle.
The following Swift 5.1 / iOS 13 UIViewController implementation shows how to use frameLayoutGuide in order to center a UIButton inside a UIScrollView.
import UIKit
class ViewController: UIViewController {
let scrollView = UIScrollView()
let button = UIButton(type: .system)
let imageView = UIImageView(image: UIImage(named: "LargeImage"))
override func viewDidLoad() {
super.viewDidLoad()
// Set scrollView constraints
scrollView.contentInsetAdjustmentBehavior = .never
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
// Set imageView constraints
scrollView.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
imageView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
imageView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
imageView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor)
])
// Set button constraints (centered)
button.setTitle("Button", for: .normal)
scrollView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: scrollView.frameLayoutGuide.centerXAnchor),
button.centerYAnchor.constraint(equalTo: scrollView.frameLayoutGuide.centerYAnchor)
])
}
}
If you want your button to stay at a fixed size and position when you modify your scrollview, the best way would be to not have it be in the scrollview at all. Add the button to the parent view of the scrollview, then it won't be affected by any changes to the scrollview and can be overlaid on top of it.
You should position your button outside the scroll view and anchor it to the bottom of the page. This way the content in your scroll view will change but your button will remain statically fixed on the view.

UIStackView - Want a percentage to define each item

I want to put two views into a UIStackView. I want the top item to always be 30% of the space no matter what device (or orientation) that it appears. Thus, I want to have the bottom view to be 70%. How do I tell a UIStackView that I want to use a percentage?
Example resulting view
Just set a constraint for the top view.
In the storyboard's Document Outline control-drag from the top view to the stack view and select Equal heights. This will make their heights equal. Now go to the Size Inspector and you should see the constraint. Double click on it and set the multiplier to 0.3. Then update the frames.
If the bottom view doesn't automatically size or it gives you an error telling that you need a height constraint, just repeat the process, but now set the multiplier to 0.7.
Swift 5.2
To implement this programmatically
Setting UIView to always be 30% of the size of it's superview within a UIStackView
viewA.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3).isActive = true
Full code implementation:
// Configure the StackView
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// Configure View A
let viewA = UIView()
viewA.backgroundColor = .darkGray
viewA.translatesAutoresizingMaskIntoConstraints = false
// Configure View B
let viewB = UIView()
viewB.backgroundColor = .lightGray
viewB.translatesAutoresizingMaskIntoConstraints = false
// Add Views to StackView
stackView.addArrangedSubview(viewA)
stackView.addArrangedSubview(viewB)
// Set the height percentage multiplier to View A
viewA.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3).isActive = true
From apple documentation
UIStackViewDistributionFillProportionally A layout where the stack
view resizes its arranged views so that they fill the available space
along the stack view’s axis. Views are resized proportionally based on
their intrinsic content size along the stack view’s axis.
So according to this you should set UIStackView distribution property to UIStackViewDistributionFillProportionally and set intrinsic size, its.
You can get more info here and on Intrinsic content size

Resources