UIScrollview does not show all subviews and is not scrollable - ios

I have some problems with scrollview. I added a ScrollView to my ViewController with simple UIViews. But the ScrollView does not scroll and it does not show all my subviews.
I followed this example IOS swift scrollview programmatically but somehow my code does not work. Here is my example
import UIKit
class StatisticsViewController: UIViewController{
let scrollView: UIScrollView = {
let view = UIScrollView()
view.backgroundColor = UIColor.lightGray.adjust(by: 28)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let topstatsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}()
let resultsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
let blue: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let yellow: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(scrollView)
// constraints of scroll view
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.addSubview(topstatsView)
scrollView.addSubview(resultsView)
scrollView.addSubview(blue)
scrollView.addSubview(yellow)
NSLayoutConstraint.activate([
topstatsView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 40),
topstatsView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 30),
topstatsView.heightAnchor.constraint(equalToConstant: 250),
topstatsView.rightAnchor.constraint(equalTo: scrollView.rightAnchor)
])
NSLayoutConstraint.activate([
resultsView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 330),
resultsView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 30),
resultsView.heightAnchor.constraint(equalToConstant: 400),
resultsView.widthAnchor.constraint(equalToConstant: 450)
])
NSLayoutConstraint.activate([
blue.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 330),
blue.leftAnchor.constraint(equalTo: resultsView.rightAnchor, constant: 20),
blue.heightAnchor.constraint(equalToConstant: 400),
blue.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
yellow.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 800),
yellow.leadingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 20),
yellow.heightAnchor.constraint(equalToConstant: 400),
yellow.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -30)
])
}
Here is a screenshot of my example.
As you can see the red view (topstatsView) does not confirm the right anchor and you cannot see the yellow and blue ones. And it is not scrollable. I am not able to see my mistakes. Thanks in advance!

Here you define wrong constraint.
1) Always add constraints related to each views top, bottom, leading and trailing, instead of define top constraint of all views to a scrollview.
2) Its not a good practise to add both leading and trailing anchor when you already define a width anchor.
3) Add bottom constraint related to a scrollview bottom to make it scrollable.
4) Add leading and trailing constraint related to outerView instead of adding it related to a scrollview.
Here is the updated code:-
class StatisticsViewController: UIViewController{
let scrollView: UIScrollView = {
let view = UIScrollView()
view.backgroundColor = UIColor.lightGray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let topstatsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}()
let resultsView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
let blue: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let yellow: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(scrollView)
// constraints of scroll view
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.addSubview(topstatsView)
scrollView.addSubview(resultsView)
scrollView.addSubview(blue)
scrollView.addSubview(yellow)
NSLayoutConstraint.activate([
topstatsView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 40),
topstatsView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 30),
topstatsView.heightAnchor.constraint(equalToConstant: 250),
topstatsView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
resultsView.topAnchor.constraint(equalTo: topstatsView.bottomAnchor, constant: 30),
resultsView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 30),
resultsView.heightAnchor.constraint(equalToConstant: 400),
resultsView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
blue.topAnchor.constraint(equalTo: resultsView.bottomAnchor, constant: 30),
blue.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 20),
blue.heightAnchor.constraint(equalToConstant: 400),
blue.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -30)
])
NSLayoutConstraint.activate([
yellow.topAnchor.constraint(equalTo: blue.bottomAnchor, constant: 30),
yellow.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
yellow.heightAnchor.constraint(equalToConstant: 400),
yellow.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -30),
yellow.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor, constant: -20)
])
}
}

Related

Combining constraints with variable and fixed heights on UIView in Swift

Update:
Thanks everyone for the unique approaches. Appreciate your insights.
Background:
I have written some code below in Xcode Swift 5 that creates four equal sized rectangles that resize depending on the device size and orientation.
However, the desired result is a rectangle with different heights, where the top rectangles are variable heights depending on the device size and orientation, and the bottom rectangles are with a constant height of 200px.
Question:
What changes to my code do I need to make to achieve variable heights on top and fixed heights on bottom?
Code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let frameTopLeft = UIView()
frameTopLeft.backgroundColor = .systemRed
view.addSubview(frameTopLeft)
frameTopLeft.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
frameTopLeft.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
frameTopLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
let frameTopRight = UIView()
frameTopRight.backgroundColor = .systemBlue
view.addSubview(frameTopRight)
frameTopRight.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
frameTopRight.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameTopRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
let frameBottomLeft = UIView()
frameBottomLeft.backgroundColor = .systemGreen
view.addSubview(frameBottomLeft)
frameBottomLeft.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomLeft.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
frameBottomLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
let frameBottomRight = UIView()
frameBottomRight.backgroundColor = .systemYellow
view.addSubview(frameBottomRight)
frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomRight.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameBottomRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
}
}
Images:
Simulator output.
Desired output.
Use StackView for better code and understanding.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let topStack = UIStackView()
topStack.axis = .horizontal
topStack.distribution = .fillEqually
let frameTopLeft = UIView()
frameTopLeft.backgroundColor = .systemRed
topStack.addArrangedSubview(frameTopLeft)
let frameTopRight = UIView()
frameTopRight.backgroundColor = .systemBlue
topStack.addArrangedSubview(frameTopRight)
let bottomStack = UIStackView()
bottomStack.axis = .horizontal
bottomStack.distribution = .fillEqually
let frameBottomLeft = UIView()
frameBottomLeft.backgroundColor = .systemGreen
bottomStack.addArrangedSubview(frameBottomLeft)
let frameBottomRight = UIView()
frameBottomRight.backgroundColor = .systemYellow
bottomStack.addArrangedSubview(frameBottomRight)
frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
frameBottomRight.heightAnchor.constraint(equalToConstant: 200).isActive = true
let mainStack = UIStackView()
mainStack.axis = .vertical
mainStack.addArrangedSubview(topStack)
mainStack.addArrangedSubview(bottomStack)
self.view.addSubview(mainStack)
mainStack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
mainStack.topAnchor.constraint(equalTo: view.topAnchor),
mainStack.heightAnchor.constraint(equalTo: view.heightAnchor),
mainStack.leftAnchor.constraint(equalTo: view.leftAnchor),
mainStack.widthAnchor.constraint(equalTo: view.widthAnchor)
])
}
}
Or you can give relative constraints to each bottom view.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let frameTopLeft = UIView()
frameTopLeft.backgroundColor = .systemRed
view.addSubview(frameTopLeft)
frameTopLeft.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
// frameTopLeft.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5), << Here
frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
frameTopLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
let frameTopRight = UIView()
frameTopRight.backgroundColor = .systemBlue
view.addSubview(frameTopRight)
frameTopRight.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
// frameTopRight.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5), << Here
frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameTopRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
let frameBottomLeft = UIView()
frameBottomLeft.backgroundColor = .systemGreen
view.addSubview(frameBottomLeft)
frameBottomLeft.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameBottomLeft.topAnchor.constraint(equalTo: frameTopLeft.bottomAnchor), // << Here
frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomLeft.heightAnchor.constraint(equalToConstant: 200), // << Here
frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
frameBottomLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
let frameBottomRight = UIView()
frameBottomRight.backgroundColor = .systemYellow
view.addSubview(frameBottomRight)
frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameBottomRight.topAnchor.constraint(equalTo: frameTopRight.bottomAnchor), // << Here
frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomRight.heightAnchor.constraint(equalToConstant: 200), // << Here
frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameBottomRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
])
}
}
You want to give the Bottom views constant Height constraints of 200, and then constrain the Bottoms of the top views to the Tops of the Bottom views.
You may find it helpful to group related code together, and add comments as you go (to make it a little clearer what you believe the code should do).
So, try it like this:
override func viewDidLoad() {
super.viewDidLoad()
// create 4 views
let frameTopLeft = UIView()
let frameTopRight = UIView()
let frameBottomLeft = UIView()
let frameBottomRight = UIView()
// set background colors
frameTopLeft.backgroundColor = .systemRed
frameTopRight.backgroundColor = .systemBlue
frameBottomLeft.backgroundColor = .systemGreen
frameBottomRight.backgroundColor = .systemYellow
// set Autoresizing and add to view
[frameTopLeft, frameTopRight, frameBottomLeft, frameBottomRight].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
NSLayoutConstraint.activate([
// top-left view constrained Top / Left / 50% Width
frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
frameTopLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
// top-right view constrained Top / Right / 50% Width
frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameTopRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
// bottom-left view constrained Bottom / Left / 50% Width
frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
frameBottomLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
// bottom-right view constrained Bottom / Right / 50% Width
frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameBottomRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
// bottom views, constant Height of 200-pts
frameBottomLeft.heightAnchor.constraint(equalToConstant: 200.0),
frameBottomRight.heightAnchor.constraint(equalToConstant: 200.0),
// constrain bottoms of top views to tops of bottom views
frameTopLeft.bottomAnchor.constraint(equalTo: frameBottomLeft.topAnchor),
frameTopRight.bottomAnchor.constraint(equalTo: frameBottomRight.topAnchor),
])
}
I'd do it like this:
let frameTopLeft = UIView()
frameTopLeft.backgroundColor = .systemRed
view.addSubview(frameTopLeft)
frameTopLeft.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
])
let frameTopRight = UIView()
frameTopRight.backgroundColor = .systemBlue
view.addSubview(frameTopRight)
frameTopRight.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameTopRight.leftAnchor.constraint(equalTo: frameTopLeft.rightAnchor),
frameTopRight.widthAnchor.constraint(equalTo: frameTopLeft.widthAnchor)
])
let frameBottomLeft = UIView()
frameBottomLeft.backgroundColor = .systemGreen
view.addSubview(frameBottomLeft)
frameBottomLeft.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameBottomLeft.topAnchor.constraint(equalTo: frameTopLeft.bottomAnchor),
frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
frameBottomLeft.heightAnchor.constraint(equalToConstant: 200),
])
let frameBottomRight = UIView()
frameBottomRight.backgroundColor = .systemYellow
view.addSubview(frameBottomRight)
frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
frameBottomRight.topAnchor.constraint(equalTo: frameTopRight.bottomAnchor),
frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
frameBottomRight.leftAnchor.constraint(equalTo: frameBottomLeft.rightAnchor),
frameBottomRight.heightAnchor.constraint(equalTo: frameBottomLeft.heightAnchor),
frameBottomRight.widthAnchor.constraint(equalTo: frameBottomLeft.widthAnchor),
])
The main diff from others is getting rid of constants as much as possible.
Your bottom views should be 200? Add single 200 constraint, and make second view height be equal to the first one.
Instead of making width equal 50% of parent, you can make left width be equal right one
It makes you code easier to maintain, decreasing places to edit.

Using ScrollView with StackView as subview and UIViews as children of StackView

I'm struggling to get my scroll view to work programatically.
I have a view controller that instantiates a UIScrollView with the following constraints
class HomeTabBarController: ViewController {
let homePageView = HomePageView()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
homePageView.setupHomePage()
view.addSubview(homePageView)
///constraints
homePageView.stackView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
homePageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
homePageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true;
homePageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true;
homePageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -83).isActive = true;
}
}
The HomePageView (UIScrollView) has a UIStackView and instantiates 3 more UIViews which are the UIStackView's children. The code and constraints are as follows
class HomePageView: UIScrollView {
var homePageCarrousel: HomePageCarrousel?
var homePageSocial: HomePageSocialUp?
var homePageAboutUs: HomePageAboutUs?
var stackView = UIStackView()
func setupHomePage() {
translatesAutoresizingMaskIntoConstraints = false
homePageCarrousel = HomePageCarrousel()
homePageSocial = HomePageSocialUp()
homePageAboutUs = HomePageAboutUs()
guard let homePageSocial = homePageSocial, let homePageCarrousel = homePageCarrousel, let homePageAboutUs = homePageAboutUs else { return }
homePageSocial.setupSocialHeader()
stackView.addArrangedSubview(homePageSocial)
homePageCarrousel.setupCarrousel()
stackView.addArrangedSubview(homePageCarrousel)
homePageAboutUs.setup()
stackView.addArrangedSubview(homePageAboutUs)
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 10
setLayout()
}
func setLayout(){
guard let homePageSocial = homePageSocial, let homePageCarrousel = homePageCarrousel, let homePageAboutUs = homePageAboutUs else { return }
///header
homePageSocial.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
homePageSocial.topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
homePageSocial.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
///carrousel
homePageCarrousel.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 20).isActive = true
homePageCarrousel.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 20).isActive = true
homePageCarrousel.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -20).isActive = true
///about us
homePageAboutUs.leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
homePageAboutUs.topAnchor.constraint(equalTo: homePageCarrousel.bottomAnchor, constant: 10).isActive = true
homePageAboutUs.trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
homePageAboutUs.bottomAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
///stackview
self.stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true;
self.stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true;
self.stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true;
self.stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true;
}
func dispose() {
homePageSocial = nil
homePageCarrousel = nil
homePageAboutUs = nil
subviews.forEach{$0.removeFromSuperview()}
}
}
Each of the children UIView (homePageSocial, homePageCarrousel and homePageAboutUs) have constraints have also got constraints:
HomePageCarrousel
Loads UIImageView and after adding as subview sets the constraint as
heightAnchor.constraint(equalToConstant: imageView.frame.height).isActive = true
HomePageAboutUs
Has 3 UITextviews (headerText, bodyTextLeft, bodyTextRight)
Header on top, bodytextLeft below it being 50% width of screen x = 0 and bodyTextRight x = width of bodyTextLeft.
Constraints are as follows
heightAnchor.constraint(equalToConstant: headerText.frame.height + bodyTextLeft.frame.height).isActive = true
bodyTextLeft.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 15).isActive = true
bodyTextLeft.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width / 2).isActive = true
bodyTextRight.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 15).isActive = true
bodyTextRight.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width / 2).isActive = true
bodyTextRight.leadingAnchor.constraint(equalTo: bodyTextLeft.trailingAnchor).isActive = true
HomePageSocialUp
A basic header with an icon and a constraint of
heightAnchor.constraint(equalToConstant: 325).isActive = true
After all that set I still can't get my ui scroll view to scroll on the y axis leaving my text of homePageAboutUs below the tabBar and off screen.
What am I doing wrong here?
Thanks in advance
First - the main purpose of a UIStackView is to arrange its subviews, so it is wrong to add position constraints to those subviews.
Next, when adding subviews to the "root" view of a controller (such as your scroll view), be sure to constrain them to the Safe Area Layout Guide.
Third, constrain the content of your scroll view to its Content Layout Guide.
I'm kind of taking your descriptions and hoping I'm close to what you're going for here:
and after scrolling down:
Here is your code, modified to produce that result:
class HomeTabBarController: UIViewController {
let homePageView = HomePageView()
override func viewDidLoad() {
super.viewDidLoad()
homePageView.setupHomePage()
view.addSubview(homePageView)
// respect safe area
let g = view.safeAreaLayoutGuide
///constraints
NSLayoutConstraint.activate([
homePageView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
homePageView.topAnchor.constraint(equalTo: g.topAnchor),
homePageView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
homePageView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
])
}
}
class HomePageSocialUp: UIView {
func setupSocialHeader() -> Void {
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .red
let imgView = UIImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imgView)
NSLayoutConstraint.activate([
// constrain image view 20-pts on each side
imgView.topAnchor.constraint(equalTo: topAnchor, constant: 20.0),
imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
imgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20.0),
imgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20.0),
// Height = 325
imgView.heightAnchor.constraint(equalToConstant: 325.0),
])
if let img = UIImage(named: "myHeaderImage") {
imgView.image = img
}
}
}
class HomePageCarrousel: UIView {
func setupCarrousel() -> Void {
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .green
let imgView = UIImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
addSubview(imgView)
NSLayoutConstraint.activate([
// constrain image view 20-pts on each side
imgView.topAnchor.constraint(equalTo: topAnchor, constant: 20.0),
imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20.0),
imgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20.0),
imgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20.0),
// let's make it 3:2 ratio
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 2.0 / 3.0)
])
if let img = UIImage(named: "myCarouselImage") {
imgView.image = img
}
}
}
class HomePageAboutUs: UIView {
let headerText = UILabel()
let bodyTextLeft = UILabel()
let bodyTextRight = UILabel()
func setup() -> Void {
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .blue
[headerText, bodyTextLeft, bodyTextRight].forEach {
// keep label height to text content
$0.setContentHuggingPriority(.required, for: .vertical)
$0.setContentCompressionResistancePriority(.required, for: .vertical)
// allow word-wrap
$0.numberOfLines = 0
// yellow background
$0.backgroundColor = .yellow
$0.translatesAutoresizingMaskIntoConstraints = false
addSubview($0)
}
NSLayoutConstraint.activate([
// header text 8-pts from Top / Leading / Trailing
headerText.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
headerText.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
headerText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
// left text 8-pts from Bottom of header text, 8-pts Leading
bodyTextLeft.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 8.0),
bodyTextLeft.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
// right text 8-pts from Bottom of header text, 8-pts Trailing
bodyTextRight.topAnchor.constraint(equalTo: headerText.bottomAnchor, constant: 8.0),
bodyTextRight.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),
// 8-pts between left and right text
bodyTextRight.leadingAnchor.constraint(equalTo: bodyTextLeft.trailingAnchor, constant: 8.0),
// left and right text equal width
bodyTextLeft.widthAnchor.constraint(equalTo: bodyTextRight.widthAnchor),
// constrain Bottom of both to <= 8 (at least 8-pts
bodyTextLeft.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0),
bodyTextRight.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -8.0),
])
headerText.font = .systemFont(ofSize: 20, weight: .regular)
bodyTextLeft.font = .systemFont(ofSize: 16, weight: .regular)
bodyTextRight.font = .systemFont(ofSize: 16, weight: .regular)
// texxt alignment
headerText.textAlignment = .center
bodyTextLeft.textAlignment = .left
bodyTextRight.textAlignment = .right
// some sample text
headerText.text = "This is the text for the About Us Header label. It will, of course, wrap onto multiple lines when needed, and auto-size it's height to fit the text."
bodyTextLeft.text = "Left label with\nembedded newlines\nso we can see it grow\nto fit the text.\nLine 5\nLine 6\nLine 7"
bodyTextRight.text = "Right label will wrap if needed. The one with the most lines will determine the bottom."
}
}
class HomePageView: UIScrollView {
var homePageCarrousel: HomePageCarrousel?
var homePageSocial: HomePageSocialUp?
var homePageAboutUs: HomePageAboutUs?
var stackView = UIStackView()
func setupHomePage() {
translatesAutoresizingMaskIntoConstraints = false
homePageCarrousel = HomePageCarrousel()
homePageSocial = HomePageSocialUp()
homePageAboutUs = HomePageAboutUs()
guard let homePageSocial = homePageSocial, let homePageCarrousel = homePageCarrousel, let homePageAboutUs = homePageAboutUs else { return }
homePageSocial.setupSocialHeader()
stackView.addArrangedSubview(homePageSocial)
homePageCarrousel.setupCarrousel()
stackView.addArrangedSubview(homePageCarrousel)
homePageAboutUs.setup()
stackView.addArrangedSubview(homePageAboutUs)
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 10
setLayout()
}
func setLayout(){
// constrain stackView to scroll view's Content Layout Guide
let g = self.contentLayoutGuide
///stackview
NSLayoutConstraint.activate([
self.stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
self.stackView.topAnchor.constraint(equalTo: g.topAnchor),
self.stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
self.stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor),
// stack view width is scroll view's frame layout guide width
stackView.widthAnchor.constraint(equalTo: self.frameLayoutGuide.widthAnchor),
])
}
}

how to make three views the same size and resized depending on the container view?

I have three views on the bottom of the view. I'm trying to make them have the same size and the size can be resized automatically depending on the size of the container view. I'd like to do it programmatically.
Here's how I want them to look
Here's how it looks by my current code
import UIKit
class ViewController: UIViewController {
var view1 = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
var view2 = UIView(frame: CGRect(x: 300, y: 300, width: 50, height: 50))
var view3 = UIView(frame: CGRect(x: 400, y: 500, width: 70, height: 70))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(view1)
view.addSubview(view2)
view.addSubview(view3)
view1.backgroundColor = .orange
view2.backgroundColor = .black
view3.backgroundColor = .gray
view1.translatesAutoresizingMaskIntoConstraints = false
view2.translatesAutoresizingMaskIntoConstraints = false
view3.translatesAutoresizingMaskIntoConstraints = false
// view1.topAnchor.constraint(equalTo: view.topAnchor, constant: 10).isActive = true
view1.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
view1.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: -10).isActive = true
view1.widthAnchor.constraint(equalTo: view.widthAnchor, constant: view.frame.size.width*0.01).isActive = true
view2.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1).isActive = true
//view2.topAnchor.constraint(equalTo: view.topAnchor, constant: 10).isActive = true
view2.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
view2.leadingAnchor.constraint(equalTo: view1.leadingAnchor, constant: view.frame.size.width*(2/3)).isActive = true
view2.widthAnchor.constraint(equalTo: view1.widthAnchor, constant: 0).isActive = true
view2.heightAnchor.constraint(equalTo: view1.heightAnchor, constant: 0).isActive = true
// view3.topAnchor.constraint(equalTo: view.topAnchor, constant: 10).isActive = true
view3.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
view3.leadingAnchor.constraint(equalTo: view2.leadingAnchor, constant: 10).isActive = true
view3.widthAnchor.constraint(equalTo: view2.widthAnchor, constant: 0).isActive = true
view3.heightAnchor.constraint(equalTo: view2.heightAnchor, constant: 0).isActive = true
}
}
Here, you can use the concept of UIStackView. Here, you can get your desired output using the following code(sample demo)
import UIKit
class ViewController: UIViewController {
var view1 = UIView()
var view2 = UIView()
var view3 = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView(arrangedSubviews: [view1, view2, view3])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.spacing = 50 //add amount of space between your views
view.addSubview(stackView)
view1.backgroundColor = .orange
view2.backgroundColor = .black
view3.backgroundColor = .gray
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
stackView.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1).isActive = true
}
}
For more about UIStackView with example check this: https://nshipster.com/uistackview
Output:
Welcome to Stackoverflow. Please read on UIStackView. And by using that stackView, you can easily achieve what you want to achieve in your screenshot.
import UIKit
class ViewController: UIViewController {
var view1 = UIView()
var view2 = UIView()
var view3 = UIView()
lazy var stackView: UIStackView = {
return UIStackView(arrangedSubviews: [
self.view1,
self.view2,
self.view3
])
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(stackView)
view1.backgroundColor = .orange
view2.backgroundColor = .black
view3.backgroundColor = .gray
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.alignment = .center
view1.translatesAutoresizingMaskIntoConstraints = false
view2.translatesAutoresizingMaskIntoConstraints = false
view3.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 16),
self.stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -16),
self.stackView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -16),
self.view1.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view1.widthAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view2.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view2.widthAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view3.heightAnchor.constraint(equalToConstant: view.frame.size.height*0.1),
self.view3.widthAnchor.constraint(equalToConstant: view.frame.size.height*0.1)
])
}
}
RESULT:

How to add extra padding to UIScrollView on iPad programmatically?

Some of the applications add extra padding on left and right to the UIScrollView instances when the device width is too large.
What's important, content of the UIScrollView is not restricted to fixed width, because user can scroll outside of the content. It looks like it would have contentInset set, which dynamically changes based on superview width. How can I achieve it?
Note
Screen from above is not implemented as a scrolling view, I just wanted to visualize the concept.
If it is an option at all, you can adjust left and right (trailing, leading) autolayout constraints.
The following code creates horizontally scrollable scrollView with two containers in it. The first container contains a subview (red rectangle) with some margins.
Scroll View with container.
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
private let scrollView = UIScrollView()
private let contentView = UIView()
override func loadView() {
let view = UIView()
self.view = view
self.view.backgroundColor = UIColor.white
scrollView.isPagingEnabled = true
scrollView.isScrollEnabled = true
scrollView.isUserInteractionEnabled = true
scrollView.bounces = true
self.view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor),
scrollView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
scrollView.rightAnchor.constraint(equalTo: self.view.rightAnchor)
])
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.backgroundColor = UIColor(white: 0.9, alpha: 0.9)
scrollView.addSubview(contentView)
let widthAnchor = contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
let heightAnchor = contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
widthAnchor.priority = .defaultLow
heightAnchor.priority = .defaultLow
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
contentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
widthAnchor,
heightAnchor
])
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.red.cgColor
let containerView1 = UIView()
containerView1.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(containerView1)
containerView1.backgroundColor = UIColor(white: 0.4, alpha: 1)
NSLayoutConstraint.activate([
containerView1.topAnchor.constraint(equalTo: contentView.topAnchor),
containerView1.leftAnchor.constraint(equalTo: contentView.leftAnchor),
containerView1.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
containerView1.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
let container1Content = container1ContentView()
containerView1.addSubview(container1Content)
NSLayoutConstraint.activate([
container1Content.topAnchor.constraint(equalTo: containerView1.topAnchor, constant: 44),
container1Content.leftAnchor.constraint(equalTo: containerView1.leftAnchor, constant: 44),
container1Content.rightAnchor.constraint(equalTo: containerView1.rightAnchor, constant: -44),
container1Content.bottomAnchor.constraint(equalTo: containerView1.bottomAnchor, constant: -44)
])
let containerView2 = UIView()
containerView2.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(containerView2)
containerView2.backgroundColor = UIColor(white: 0.9, alpha: 1)
NSLayoutConstraint.activate([
containerView2.topAnchor.constraint(equalTo: contentView.topAnchor),
containerView2.leftAnchor.constraint(equalTo: containerView1.rightAnchor),
containerView2.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
containerView2.rightAnchor.constraint(equalTo: contentView.rightAnchor),
containerView2.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
}
private func container1ContentView() -> UIView {
let content = UIView()
content.translatesAutoresizingMaskIntoConstraints = false
content.backgroundColor = UIColor.red
return content
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

Using ScrollView Programmatically in Swift 3

I have searched other questions and seem to still have some trouble creating my scrollView programmatically with autolayout in swift 3. I am able to get my scrollview to show up as shown in the picture below, but when I scroll to the bottom my other label does not show up and the 'scroll top' label does not disappear.
Hoping someone can help review my code below!
import UIKit
class ViewController: UIViewController {
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
let screensize: CGRect = UIScreen.main.bounds
let screenWidth = screensize.width
let screenHeight = screensize.height
var scrollView: UIScrollView!
scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
scrollView.addSubview(labelOne)
scrollView.addSubview(labelTwo)
view.addSubview(labelOne)
view.addSubview(labelTwo)
view.addSubview(scrollView)
// Visual Format Constraints
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-100-[v0]", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": labelOne]))
// Using iOS 9 Constraints in order to place the label past the iPhone 7 view
view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .top, relatedBy: .equal, toItem: labelOne, attribute: .bottom, multiplier: 1, constant: screenHeight + 200))
view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .right, relatedBy: .equal, toItem: labelOne, attribute: .right, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: labelTwo, attribute: .left, relatedBy: .equal, toItem: labelOne, attribute: .left, multiplier: 1, constant: 0)
}
}
It is easy to use constraints to define the scroll content size - so you don't have to do any manual calculations.
Just remember:
The content elements of your scroll view must have left / top / width / height values. In the case of objects such as labels, they have intrinsic sizes, so you only have to define the left & top.
The content elements of your scroll view also define the bounds of the scrollable area - the contentSize - but they do so with the bottom & right constraints.
Combining those two concepts, you see that you need a "continuous chain" with at least one element defining the top / left / bottom / right extents.
Here is a simple example, that will run directly in a Playground page:
import UIKit
import PlaygroundSupport
class TestViewController : UIViewController {
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
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 labelOne to the scroll view
scrollView.addSubview(labelOne)
// constrain labelOne to left & top with 16-pts padding
// this also defines the left & top of the scroll content
labelOne.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 16.0).isActive = true
labelOne.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 16.0).isActive = true
// add labelTwo to the scroll view
scrollView.addSubview(labelTwo)
// constrain labelTwo at 400-pts from the left
labelTwo.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 400.0).isActive = true
// constrain labelTwo at 1000-pts from the top
labelTwo.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 1000).isActive = true
// constrain labelTwo to right & bottom with 16-pts padding
labelTwo.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -16.0).isActive = true
labelTwo.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -16.0).isActive = true
}
}
let vc = TestViewController()
vc.view.backgroundColor = .yellow
PlaygroundPage.current.liveView = vc
Edit - since this answer still gets occasional attention, I've updated the code to use more modern syntax, to respect the safe-area, and to use the scroll view's .contentLayoutGuide:
class TestViewController : UIViewController {
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .yellow
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let scrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// add the scroll view to self.view
self.view.addSubview(scrollView)
// add labelOne to the scroll view
scrollView.addSubview(labelOne)
// add labelTwo to the scroll view
scrollView.addSubview(labelTwo)
// always a good idea to respect safe area
let safeG = view.safeAreaLayoutGuide
// we want to constrain subviews to the scroll view's Content Layout Guide
let contentG = scrollView.contentLayoutGuide
NSLayoutConstraint.activate([
// constrain the scroll view to safe area with 8-pts on each side
scrollView.topAnchor.constraint(equalTo: safeG.topAnchor, constant: 8.0),
scrollView.leadingAnchor.constraint(equalTo: safeG.leadingAnchor, constant: 8.0),
scrollView.trailingAnchor.constraint(equalTo: safeG.trailingAnchor, constant: -8.0),
scrollView.bottomAnchor.constraint(equalTo: safeG.bottomAnchor, constant: -8.0),
// constrain labelOne to leading & top of Content Layout Guide with 16-pts padding
// this also defines the left & top of the scroll content
labelOne.topAnchor.constraint(equalTo: contentG.topAnchor, constant: 16.0),
labelOne.leadingAnchor.constraint(equalTo: contentG.leadingAnchor, constant: 16.0),
// constrain labelTwo leading at 400-pts from labelOne trailing
labelTwo.leadingAnchor.constraint(equalTo: labelOne.trailingAnchor, constant: 400.0),
// constrain labelTwo top at 1000-pts from the labelOne bottom
labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 1000),
// constrain labelTwo to trailing & bottom of Content Layout Guide with 16-pts padding
// this also defines the right & bottom of the scroll content
labelTwo.trailingAnchor.constraint(equalTo: contentG.trailingAnchor, constant: -16.0),
labelTwo.bottomAnchor.constraint(equalTo: contentG.bottomAnchor, constant: -16.0),
])
}
}
Two things.
1. Add the labels to scroll view, not your view
You want your label to scroll with scroll view, then you should not add it on your view. When running your code, you can scroll but the fixed label there is pinned to your view, not on your scroll view
2. Make sure you added your constraints correctly
Try it on your storyboard about what combination of constraint is enough for a view. At least 4 constraints are needed for a label.
Bottom line
Here is a modified version of your code. For constraint I added padding left, padding top, width and height and it works. My code is
let labelOne: UILabel = {
let label = UILabel()
label.text = "Scroll Top"
label.backgroundColor = .red
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelTwo: UILabel = {
let label = UILabel()
label.text = "Scroll Bottom"
label.backgroundColor = .green
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
let screensize: CGRect = UIScreen.main.bounds
let screenWidth = screensize.width
let screenHeight = screensize.height
var scrollView: UIScrollView!
scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
scrollView.addSubview(labelTwo)
NSLayoutConstraint(item: labelTwo, attribute: .leading, relatedBy: .equal, toItem: scrollView, attribute: .leadingMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: labelTwo, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: labelTwo, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .topMargin, multiplier: 1, constant: 10).isActive = true
NSLayoutConstraint(item: labelTwo, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
view.addSubview(scrollView)
}
And the scroll view looks like this
For me this work like charm
class WithDrawConfirmationViewController: UIViewController, WithDrawConfirmationViewProtocol {
var presenter: WithDrawConfirmationPresenterProtocol?
private let viewbackgroundColor = UIColor(hexString: "#F5F5F8")
// MARK:- View properties
lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
//view.backgroundColor = .red
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
lazy var contentView: UIView = {
let view = UIView()
//view.backgroundColor = .green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var headerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var titleLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeBlack
label.text = "Withdraw Confirmation"//"Loading.."
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-DemiBold", size: 20)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}()
private lazy var descriptionLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeGray
label.text = "Please confirm your fixed deposit details before withdrawing your deposit"// "Loading.."
label.textAlignment = .center
label.font = UIFont(name: "AvenirNext-Medium", size: 14)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.setContentHuggingPriority(1000, for: .vertical)
return label
}()
private lazy var instructionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeGray
label.text = "Complete fixed deposit will be withdrawn and the money will be debited to your account within 1-2 working days."//"Loading.."
label.textAlignment = .left
label.font = UIFont(name: "AvenirNext-Medium", size: 14)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}()
private lazy var warningLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.themeBlue
label.text = "Axis bank will levy a penalty of 1% - 2% for premature withdrawal"//"Loading.."
label.textAlignment = .left
label.font = UIFont(name: "AvenirNext-DemiBold", size: 13)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}()
private lazy var warningView: UIView = {
let view = UIView()
view.cornerradius = 5
view.backgroundColor = UIColor(hexFromString: "#E8F4FD")
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var fdInformationDetailView: FDPurchaseDetailView = {
let view = FDPurchaseDetailView(isPoweredByViewVisible: false)
//view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var footerCTAView: FooterButtonViewView = {
let view = FooterButtonViewView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// MARK:- Life cycle
override func viewDidLoad() {
super.viewDidLoad()
contentView.backgroundColor = viewbackgroundColor
scrollView.backgroundColor = viewbackgroundColor
setUpUI()
presenter?.viewDidLoadTriggered()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
private func setUpUI() {
view.addSubview(scrollView)
scrollView.addSubview(contentView)
view.backgroundColor = viewbackgroundColor
addFooter()
setUpHeaderView()
setUpFDDetailView()
setUpInformationAndWarningView()
//scrollView.backgroundColor = .lightGray
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 100, right: 0)
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
//contentView.bottomAnchor.constraint(equalTo: footerCTAView.topAnchor).isActive = true
}
private func setUpHeaderView() {
contentView.addSubview(headerView)
headerView.addSubviews([titleLabel, descriptionLabel])
headerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
headerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
headerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
titleLabel.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 8).isActive = true
descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 20).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 8).isActive = true
descriptionLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true
descriptionLabel.bottomAnchor.constraint(equalTo: headerView.bottomAnchor, constant: -20).isActive = true
}
private func setUpFDDetailView() {
contentView.addSubview(fdInformationDetailView)
fdInformationDetailView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true
fdInformationDetailView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
fdInformationDetailView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
}
private func setUpInformationAndWarningView() {
contentView.addSubview(instructionLabel)
instructionLabel.topAnchor.constraint(equalTo: fdInformationDetailView.bottomAnchor, constant: 20).isActive = true
instructionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
instructionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
warningView.addSubview(warningLabel)
warningLabel.topAnchor.constraint(equalTo: warningView.topAnchor, constant: 8).isActive = true
warningLabel.leadingAnchor.constraint(equalTo: warningView.leadingAnchor, constant: 20).isActive = true
warningLabel.trailingAnchor.constraint(equalTo: warningView.trailingAnchor, constant: -20).isActive = true
warningLabel.bottomAnchor.constraint(equalTo: warningView.bottomAnchor, constant: -8).isActive = true
contentView.addSubview(warningView)
warningView.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 8).isActive = true
warningView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true
warningView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
//warningView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
warningView.bottomAnchor.constraint(greaterThanOrEqualTo: contentView.bottomAnchor, constant: -20).isActive = true
}
private func addFooter() {
view.addSubview(footerCTAView)
footerCTAView.delegate = self
footerCTAView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
footerCTAView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
footerCTAView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
Set scrollview images to the wallpaper:
#IBOutlet var scroll_view_img: UIScrollView!
var itemPhotoList = NSMutableArray()
var button = NSMutableArray()
#IBOutlet var imageview_big: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
itemPhotoList = ["grief-and-loss copy.jpg","aaa.jpg","image_4.jpeg"]
// button = ["btn1","btn2"]
let width:CGFloat = 100
let height:CGFloat = 100
var xposition:CGFloat = 10
var scroll_contont:CGFloat = 0
for i in 0 ..< itemPhotoList.count
{
var button_img = UIButton()
button_img = UIButton(frame: CGRect(x: xposition, y: 50, width: width, height: height))
let img = UIImage(named:itemPhotoList[i] as! String)
button_img.setImage(img, for: .normal)
scroll_view_img.addSubview(button_img)
button_img.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button_img.tag = i
view.addSubview(scroll_view_img)
xposition += width+10
scroll_contont += width
scroll_view_img.contentSize = CGSize(width: scroll_contont, height: height)
}
}
func buttonAction(sender: UIButton!)
{
switch sender.tag {
case 0:
imageview_big.image = UIImage(named: "grief-and-loss copy.jpg")
case 1:
imageview_big.image = UIImage(named: "aaa.jpg")
case 2:
imageview_big.image = UIImage(named: "image_4.jpeg")
default:
break
}
}
Copy and paste this controller in your project
class BaseScrollViewController: UIViewController {
lazy var contentViewSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 100)
lazy var scrollView: UIScrollView = {
let view = UIScrollView(frame: .zero)
view.backgroundColor = .white
view.frame = self.view.bounds
view.contentSize = contentViewSize
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
lazy var containerView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.frame.size = contentViewSize
return v
}()
override func viewDidLoad() {
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.addSubview(containerView)
setupContainer(containerView)
super.viewDidLoad()
}
public func setupContainer(_ container: UIView) {
}
}
Usage for above code:
class ClientViewController: BaseScrollViewController {
override func viewDidLoad() {
super.viewDidLoad()
// do your stuff here
}
override func setupContainer(_ container: UIView) {
// add views here
}
}
These answers do not work with large titles in the navigation bar. Make sure you have the code below in your viewDidLoad() method of your view controller:
self.navigationController?.navigationBar.prefersLargeTitles = false

Resources