My code below is trying to load a gif on a image view. This code works when the code use the constraints on a storyboard however when I write the nslayout constraints the gif does not appear. There is no runtime or compile error the gif is just not appearing.
let FIRE = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
FIRE.loadGif(name: "FIRE")
view.addSubview(FIRE)
FIRE.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
FIRE.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
FIRE.widthAnchor.constraint(equalToConstant: 400).isActive = true
FIRE.heightAnchor.constraint(equalToConstant: 80).isActive = true
}
You need to disable the view's autoresizing mask to set constraints manually:
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
// 1) turn off autoresizing mask
imageView.translatesAutoresizingMaskIntoConstraints = false
// 2) set contraints
self.setupSubviews()
// 3) update the view
imageView.loadGif(name: "FIRE")
}
func setupSubviews() {
self.view.addSubview(imageView)
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 400).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 80).isActive = true
}
Related
recently I tried to set up a dynamic UIScrollView according to this website : https://medium.com/#javedmultani16/uiscrollview-dynamic-content-size-through-storyboard-in-ios-fb873e9278e
I tried to make one step by step, but seems like I have misunderstood something but I can't figure it out. Here is my code, I tried to add 30 UITextField and set the UIScrollView equal height with those contents.
Problem I met, the scrollview not work correctly, it can only scroll a little bit, like I can only scroll to about 8 or 9 line , the others below I can't scroll down.
override func viewDidLoad() {
super.viewDidLoad()
//Step 1
let scrollview = UIScrollView()
view.addSubview(scrollview)
scrollview.translatesAutoresizingMaskIntoConstraints = false
scrollview.topAnchor.constraint(equalTo: view.topAnchor).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
scrollview.alwaysBounceVertical = true
//Step 2
let oneview = UIView()
scrollview.addSubview(oneview)
oneview.translatesAutoresizingMaskIntoConstraints = false
oneview.topAnchor.constraint(equalTo: scrollview.topAnchor).isActive = true
oneview.bottomAnchor.constraint(equalTo: scrollview.bottomAnchor).isActive = true
oneview.leadingAnchor.constraint(equalTo: scrollview.leadingAnchor).isActive = true
oneview.trailingAnchor.constraint(equalTo: scrollview.trailingAnchor).isActive = true
oneview.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
let heightConstraint = oneview.heightAnchor.constraint(equalTo: view.heightAnchor)
heightConstraint.isActive = true
heightConstraint.priority = .defaultLow
//Step 3
for i in 1...30{
let field = UITextField()
field.placeholder = "This is line "+String(i+1)
field.backgroundColor = .gray
field.isUserInteractionEnabled = false
oneview.addSubview(field)
field.translatesAutoresizingMaskIntoConstraints = false
field.widthAnchor.constraint(equalTo: oneview.widthAnchor, multiplier: 0.8).isActive = true
field.heightAnchor.constraint(equalToConstant: 50).isActive = true
field.centerXAnchor.constraint(equalTo: oneview.centerXAnchor).isActive = true
field.topAnchor.constraint(equalTo: oneview.topAnchor, constant: CGFloat(100*i)).isActive = true
}
}
The whole point of using this dummy subview (oneview in your code) is so that its contents can tell the dummy subview what height it should be, and the scroll view's content size will fit that height.
However, your textfield's constraints do not suggest a height for oneview!. All your textfields' constraints say is the text fields
should be some distance below the top of oneview.
should be a certain height
should have the same centre X as oneview
should have the same width as oneview
To satisfy the above, oneview's height doesn't need to change at all. The layout engine can just place your text fields outside of the bounds of oneview, and still satisfy the above constraints. (Think about it!)
But, if you add one more constraint to the last text field, that it
should be a certain distance above the bottom of oneview
then oneview has to resize in order to satisfy that. You are indirectly implying a height for oneview, because you are saying
for the last text field, I want this much space to be above it, and this much space to be below it.
We could just make that "certain distance" zero as well. Here's how you would do it in code:
// in the loop...
if i == 30 {
field.bottomAnchor.constraint(equalTo: oneview.bottomAnchor).isActive = true
}
An even better way to tell oneview its height would be to use a UIStackView. Replace steps 2 and 3 with:
let oneview = UIStackView()
oneview.alignment = .fill
oneview.distribution = .equalSpacing
oneview.spacing = 50
oneview.axis = .vertical
scrollview.addSubview(oneview)
oneview.translatesAutoresizingMaskIntoConstraints = false
oneview.topAnchor.constraint(equalTo: scrollview.topAnchor).isActive = true
oneview.bottomAnchor.constraint(equalTo: scrollview.bottomAnchor).isActive = true
oneview.leadingAnchor.constraint(equalTo: scrollview.leadingAnchor).isActive = true
oneview.trailingAnchor.constraint(equalTo: scrollview.trailingAnchor).isActive = true
oneview.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
let heightConstraint = oneview.heightAnchor.constraint(equalTo: view.heightAnchor, constant: 25)
heightConstraint.isActive = true
heightConstraint.priority = .defaultLow
for i in 1...30{
let field = UITextField()
field.placeholder = "This is line "+String(i+1)
field.backgroundColor = .gray
field.isUserInteractionEnabled = false
oneview.addArrangedSubview(field)
field.translatesAutoresizingMaskIntoConstraints = false
field.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
The problem is the first top view should be anchored to the top of the content view and the last bottom view should anchored to the bottom of the contentView
Extensions
extension UIScrollView {
func assignKeyboardObservers() {
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillDissmiss),
name: UIResponder.keyboardWillHideNotification,
object: nil
)
}
#objc fileprivate func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
insetScrollView(insetBottom: keyboardHeight)
}
}
#objc fileprivate func keyboardWillDissmiss(_ notification: Notification) {
insetScrollView(insetBottom: 0)
}
fileprivate func insetScrollView(insetBottom: CGFloat) {
var inset:UIEdgeInsets = contentInset
inset.bottom = insetBottom
UIView.animate(withDuration: 0.2, animations: {
self.contentInset = inset
})
}
}
extension UIViewController {
func addScrollViewToView(paddingContentView:UIEdgeInsets = .zero, anchorScroll:( _ scrollView: inout UIScrollView)->())->UIView {
let tapToDismiss = UITapGestureRecognizer(target: self, action: #selector(dimissKeyboard(_:)))
tapToDismiss.cancelsTouchesInView = false
view.addGestureRecognizer(tapToDismiss)
var scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.bounces = true
scrollView.isScrollEnabled = true
scrollView.assignKeyboardObservers()
let contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
anchorScroll(&scrollView)
scrollView.addSubview(contentView)
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: paddingContentView.top),
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: paddingContentView.left),
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -paddingContentView.right),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -paddingContentView.bottom),
contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
return contentView
}
#objc fileprivate func dimissKeyboard(_ sender: UITapGestureRecognizer) {
view.endEditing(true)
}
}
How to use it
import UIKit
class TESTViewController: UIViewController {
private var contentView:UIView!
override func viewDidLoad() {
super.viewDidLoad()
contentView = addScrollViewToView(anchorScroll: { (scrollView) in
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
})
let stackView:UIStackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 3
stackView.alignment = .fill
stackView.distribution = .fillEqually
for i in 1...30{
let field = UITextField()
field.translatesAutoresizingMaskIntoConstraints = false
field.placeholder = "This is line "+String(i+1)
field.backgroundColor = .gray
field.isUserInteractionEnabled = false
field.heightAnchor.constraint(equalToConstant: 50).isActive = true
stackView.addSubview(field)
}
contentView.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: contentView.topAnchor),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
])
}
}
I have some issues using translatesAutoresizingMaskIntoConstraints.
I am creating a scrollview where you can swipe to the left or right. Every time you get a another image. But for some reason I can't swipe if translatesAutoresizingMaskIntoConstraints = false.
This is with translatesAutoresizingMaskIntoConstraints = false (in this case, I can't swipe left or right)
But if I set translatesAutoresizingMaskIntoConstraints = true it look like this and I can swipe to the left and right. The problem is, that the layout is messed up.
My code:
var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.alwaysBounceVertical = false
scrollView.alwaysBounceHorizontal = false
scrollView.isPagingEnabled = true
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.frame = contentView.frame
contentView.addSubview(scrollView)
scrollView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0).isActive = true
scrollView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
scrollView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true
}
var frame = CGRect.zero
func viewTutorial() {
for i in 0..<arrayOfTutorilImages.count {
frame.origin.x = scrollView.frame.size.width * CGFloat((i))
frame.size = scrollView.frame.size
let imageView = UIImageView(frame: frame)
imageView.image = UIImage(named: arrayOfTutorilImages[i])
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .red
imageView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.addSubview(imageView)
imageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0).isActive = true
imageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
imageView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0).isActive = true
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true
}
scrollView.contentSize = CGSize(width: (scrollView.frame.size.width * CGFloat(arrayOfTutorilImages.count)), height: scrollView.frame.size.height)
scrollView.delegate = self
}
extension TutorialViewController: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(pageNumber)
}
}
You need to add constraints probably , You can try
class TutorialViewController: UIViewController {
var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.showsHorizontalScrollIndicator = false
scrollView.showsVerticalScrollIndicator = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.alwaysBounceVertical = false
scrollView.alwaysBounceHorizontal = false
scrollView.isPagingEnabled = true
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
contentView.addSubview(scrollView)
scrollView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 0).isActive = true
scrollView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
scrollView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: 0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true
viewTutorial()
}
func viewTutorial() {
var con:UIView = scrollView
for i in 0..<arrayOfTutorilImages.count {
let imageView = UIImageView(frame: CGRect.zero)
imageView.image = UIImage(named: arrayOfTutorilImages[i])
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .red
imageView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.addSubview(imageView)
if con == scrollView {
imageView.leftAnchor.constraint(equalTo: con.leftAnchor, constant: 0).isActive = true
}
else {
imageView.leftAnchor.constraint(equalTo: con.rightAnchor, constant: 0).isActive = true
}
imageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
if i == arrayOfTutorilImages.count - 1 {
imageView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
}
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
imageView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
con = imageView
}
scrollView.delegate = self
}
}
extension TutorialViewController: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
}
}
Don't set frames when using constrraints
Add Scrollview frame when create
var scrollView: UIScrollView = {
let scrollView = UIScrollView(frame:.zero)
//other properties
return scrollView
}()
ImageView constraints same as for ScrollView.
That is the reason why you can't swipe, because all your images have the same constraints, and they are located one over another.
You should do smth like:
imageView.translatesAutoresizingMaskIntoConstraints = true
imageView.frame.size = contentView.frame.size
imageView.frame.origin = CGPoint(x: imageIndex * contentView.frame.size.width, y: 0.0)
I am trying to setup a couple of views programmatically. On my main view I add two subviews, one anchored to the top and one to the bottom:
//Button View
view.addSubview(buttonsLabel)
buttonsLabel.translatesAutoresizingMaskIntoConstraints = false
buttonsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
buttonsLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
buttonsLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20).isActive = true
buttonsLabel.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5, constant: -20).isActive = true
//Calculator View
calcLabel.layer.cornerRadius = 25
view.addSubview(calcLabel)
calcLabel.translatesAutoresizingMaskIntoConstraints = false
calcLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
calcLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
calcLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 40).isActive = true
//calcLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20).isActive = true
calcLabel.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5, constant: -40).isActive = true
This works fine, both views are 50% of the frame height (minus the constants) and both are shown (one at the top, one at the bottom). But when I try to add a third view, which is 75% of the frames height and which should be placed on top of the other two views, the layout is destroyed and everything is moved almost outside of the frame.
I am trying to anchor the third view to the bottom again:
thirdView.layer.cornerRadius = 25
view.addSubview(thirdView)
thirdView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
thirdView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
thirdView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
thirdView.heightAnchor.constraint(equalTo: view.heightAnchor,multiplier: 0.75).isActive = true
This is how everything should look like (left the two views, right the third view on top:
Am I doing the anchors and constraints right (or whats abetter way) and how to add the constraint for the third view, so that it is 75% of the frames height and placed like in the image on top of everything.
Your code looks good the problem is else where, check the view hierarchy in the debugger to see which constraint(s) failed, perhaps you forgot translatesAutoresizingMaskIntoConstraints as beyowulf commented. you should be using constants as well, this makes code much more maintainable.
here is my implementation:
import UIKit
class ViewController: UIViewController {
//MARK: - SubViews
let topHalfView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.gray
return view
}()
let bottomHalfView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.gray
return view
}()
let threeQuarterView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.black
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//add, layout subviews with 9+ constraints
setupViews()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupViews() {
self.view.addSubview(topHalfView)
self.view.addSubview(bottomHalfView)
self.view.addSubview(threeQuarterView)
let guide = self.view.safeAreaLayoutGuide
let spacing:CGFloat = 12
let viewHeight = self.view.frame.height - spacing
topHalfView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
topHalfView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: spacing).isActive = true
topHalfView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -spacing).isActive = true
topHalfView.heightAnchor.constraint(equalToConstant: viewHeight * 0.5).isActive = true
bottomHalfView.topAnchor.constraint(equalTo: topHalfView.bottomAnchor, constant: spacing).isActive = true
bottomHalfView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: spacing).isActive = true
bottomHalfView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -spacing).isActive = true
bottomHalfView.heightAnchor.constraint(equalToConstant: viewHeight * 0.5).isActive = true
threeQuarterView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
threeQuarterView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: spacing).isActive = true
threeQuarterView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -spacing).isActive = true
threeQuarterView.heightAnchor.constraint(equalToConstant: self.view.frame.height * 0.75).isActive = true
}
}
The View hierarchy:
I have this UIScrollView set up with a UIImageView and a UILabel.
However, it's not scrolling like I want it to. It's like it's not even there... How can I fix this? I just want it to be scrollable (even though theres nothing at the bottom to scroll to yet)
Thx in advance!
Here's my code:
// scrollView
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
// scrollView constraints
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
// Initialize profileImageView
profileImageView.image = #imageLiteral(resourceName: "neil")
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.layer.cornerRadius = 125 / 2
profileImageView.clipsToBounds = true
scrollView.addSubview(profileImageView)
// Add profileImageView constraints
profileImageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 45).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 125).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 125).isActive = true
profileImageView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
// Add separator view
seperator.translatesAutoresizingMaskIntoConstraints = false
seperator.backgroundColor = UIColor.darkGray
scrollView.addSubview(seperator)
// seperator constraints
seperator.heightAnchor.constraint(equalToConstant: 2).isActive = true
seperator.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.8).isActive = true
seperator.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
seperator.topAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 20).isActive = true
//nameLabel
let nameLabelFont = UIFont.monospacedDigitSystemFont(ofSize: 36, weight: UIFont.Weight.heavy)
nameLabel.font = nameLabelFont
nameLabel.text = currentUser.name
nameLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(nameLabel)
// nameLabel constraints
nameLabel.topAnchor.constraint(equalTo: seperator.bottomAnchor, constant: 10).isActive = true
nameLabel.leadingAnchor.constraint(equalTo: seperator.leadingAnchor).isActive = true
You can't scroll because scrollView.contentSize isn't bigger then your view.
If you want make your screen scrollable you should set scrollView.contentSize.
Put scrollView.contentSize = CGSize(width: 1000, height: 1000) before view.addSubview(scrollView) to set your scrollable area to 1000x1000.
Generally scrollView.contentSize should be calculated base on UI elements inside UIScrollView e.g. size of 10 images with space between them.
I am trying to figure out how UIScrollView works and I added some subviews to it with different backgroundColor properties. I layed out the subviews with ios9 autolayout but even if the views are outside of the screen, the UIScrollView still does not scroll.
import UIKit
class ViewController: UIViewController {
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.translatesAutoresizingMaskIntoConstraints = false
sv.backgroundColor = .gray
return sv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
let view1 = UIView()
view1.backgroundColor = .red
let view2 = UIView()
view2.backgroundColor = .blue
let view3 = UIView()
view3.backgroundColor = .green
let view4 = UIView()
view4.backgroundColor = .purple
let views = [view1, view2, view3, view4]
for view in views {
scrollView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
}
view1.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
view1.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
view1.heightAnchor.constraint(equalToConstant: 140).isActive = true
view1.widthAnchor.constraint(equalToConstant: 140).isActive = true
view2.topAnchor.constraint(equalTo: view1.bottomAnchor, constant: 100).isActive = true
view2.leftAnchor.constraint(equalTo: view1.rightAnchor).isActive = true
view2.heightAnchor.constraint(equalToConstant: 140).isActive = true
view2.widthAnchor.constraint(equalToConstant: 140).isActive = true
view3.topAnchor.constraint(equalTo: view2.bottomAnchor, constant: 50).isActive = true
view3.leftAnchor.constraint(equalTo: view1.rightAnchor).isActive = true
view3.heightAnchor.constraint(equalToConstant: 140).isActive = true
view3.widthAnchor.constraint(equalToConstant: 140).isActive = true
view4.topAnchor.constraint(equalTo: view3.bottomAnchor, constant: 20).isActive = true
view4.leftAnchor.constraint(equalTo: view1.rightAnchor).isActive = true
view4.heightAnchor.constraint(equalToConstant: 140).isActive = true
view4.widthAnchor.constraint(equalToConstant: 140).isActive = true
}
}
When using Autolayout in UIScrollViews you have to pin subviews both to the top and bottom of the scrollview which allows the scrollview to calculate its contentSize.
Adding this line fixes it:
view4.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true