Custom UIView constraints not being applied - ios

I have the following custom UIView:
import UIKit
class CustomView: UIView {
// container view
lazy var contView: UIView = {
let cv = UIView()
cv.backgroundColor = .gray
cv.translatesAutoresizingMaskIntoConstraints = false
cv.layer.cornerRadius = 5
cv.layer.masksToBounds = true
cv.isUserInteractionEnabled = true
return cv
}()
lazy var t: UIImageView = {
let tt = UIImageView()
tt.translatesAutoresizingMaskIntoConstraints = false
tt.isUserInteractionEnabled = false
tt.contentMode = .scaleAspectFill
tt.alpha = 0
return tt
}()
let img: UIImageView = {
let image = UIImageView()
image.backgroundColor = .yellow
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFill
return image
}()
let ny: UITextView = {
let info = UITextView()
info.translatesAutoresizingMaskIntoConstraints = false
info.backgroundColor = .blue
info.text = "Some, Text"
info.textColor = .white
return info
}()
func setupContView() {
contView.addSubview(img)
contView.addSubview(ny)
img.addSubview(t)
// UIViewAlertForUnsatisfiableConstraints error
contView.widthAnchor.constraint(equalTo: self.widthAnchor, constant: -24).isActive = true
contView.heightAnchor.constraint(equalTo: self.heightAnchor, constant: -200).isActive = true
contView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
contView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
t.centerXAnchor.constraint(equalTo: img.centerXAnchor).isActive = true
t.centerYAnchor.constraint(equalTo: img.centerYAnchor).isActive = true
img.centerXAnchor.constraint(equalTo: contView.centerXAnchor).isActive = true
img.centerYAnchor.constraint(equalTo: contView.centerYAnchor).isActive = true
img.widthAnchor.constraint(equalTo: contView.widthAnchor).isActive = true
img.heightAnchor.constraint(equalTo: contView.heightAnchor).isActive = true
ny.leftAnchor.constraint(equalTo: contView.leftAnchor).isActive = true
ny.rightAnchor.constraint(equalTo: contView.rightAnchor).isActive = true
ny.bottomAnchor.constraint(equalTo: contView.bottomAnchor).isActive = true
ny.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(contView)
setupContView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
When I try to create a reference to this CustomView object in my view controller like so...
var myCustomView = CustomView()
... None of the constraints are applied, and nothing appears. When I try to add constraints in the view controller or CustomView to center myCustomView I get UnsatisfiableConstraints errors. They appear when I add color to them and stuff in the view controller but I already did that in the custom UIView class so its redundant and overrides what I did.
What do I do? I want to be able to create objects of my custom class and have them all look the same, but none of the constraints or custom views I create in my CustomView class seem to be doing anything.

Related

addSubview doesn't work in UIView - Swift Programmatically

I need to add a UIButton as a subview on a UIView but it actually doesn't appear at runtime.
This is my code:
let moreButton : UIButton = {
let button = UIButton()
button.setImage(#imageLiteral(resourceName: "more"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(moreButton)
moreButton.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
moreButton.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
moreButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
moreButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
}
The button isn't added eventually to the view. I'm sure this is an easy fix but I can't wrap my head around it.
First of all, make sure you the viewController is showing anything since you're doing it without storyboards, check out this simple tutorial:
https://medium.com/better-programming/creating-a-project-without-storyboard-in-2020-and-without-swifui-82080eb6d13b
If the problem is with that UIButton, try to set up the subviews in viewDidLoad:
final class ViewController: UIViewController {
let cardView = CardView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(cardView)
/// Constraints
let margins = view.layoutMarginsGuide
cardView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
cardView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
cardView.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
cardView.heightAnchor.constraint(equalToConstant: 30).isActive = true
cardView.widthAnchor.constraint(equalToConstant: 20).isActive = true
}
}
final class CardView: UIView {
let moreButton : UIButton = {
let button = UIButton()
button.setTitle("Button title", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override init(frame: CGRect) {
super.init(frame:frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(moreButton)
/// Constraints
let margins = self.layoutMarginsGuide
moreButton.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
moreButton.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
moreButton.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
moreButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
moreButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
}
}
I've set up margins for the constraints and also added a leadingAnchor constraint, that might have been the issue as well.

Add UILabel as subview of UITextField on top

I am in the process of implementing a UILabel as a subview of a UITextField which will be shown right above the UITextField itself. The UITextField has a rounded border and what I would like to achieve is the UILabel to be shown over the border.
Everything currently works as expected, but the UILabel is drawn behind the border of the UITextField. I want it to go "over" (above) the border so the white backgroundColor would be shown above part of the border and make the text more easily readible.
var priceTextField: CustomTextField = {
let priceTextField = CustomTextField()
priceTextField.layer.cornerRadius = 10.0
priceTextField.layer.borderWidth = 1.0
priceTextField.layer.borderColor = UIColor.darkGray.cgColor
priceTextField.translatesAutoresizingMaskIntoConstraints = false
priceTextField.font = UIFont.systemFont(ofSize: 15)
priceTextField.textColor = .black
priceTextField.text = "0"
priceTextField.suffix = "EUR"
priceTextField.suffixTextColor = .darkGray
priceTextField.suffixSpacing = 2.0
priceTextField.textAlignment = .center
priceTextField.labelText = "Price"
return priceTextField
}()
In my CustomTextField class (subclass of UITextField):
public var labelText: String?
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.textAlignment = .center
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .lightGray
topLabel.backgroundColor = .white
topLabel.numberOfLines = 1
return topLabel
}()
func setupLabel() {
self.addSubview(topLabel)
topLabel.centerYAnchor.constraint(equalTo: self.topAnchor).isActive = true
topLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
topLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20).isActive = true
topLabel.text = labelText
}
I call setupLabel() at the end of the draw(_ rect: CGRect) method of UITextField (because I work with this to show the EUR sign always behind the entered value).
I have tried to play around with bringSubviewToFront and changing the zPosition of the layer of the UILabel, without success.
It now looks like this:
How can I bring the text "above" the border on the top?
EDIT: Tried Sh_Khan's solution, but it's still hidden behind the border.
import Foundation
import UIKit
public class CustomTextView: UIView, UITextFieldDelegate {
public var labelText: String?
var customTextField: CustomTextField = {
let customTextField = CustomTextField()
customTextField.translatesAutoresizingMaskIntoConstraints = false
customTextField.font = UIFont.systemFont(ofSize: 15)
customTextField.textColor = .black
customTextField.textAlignment = .center
customTextField.text = "0"
customTextField.suffix = "EUR"
customTextField.suffixTextColor = .lightGray
customTextField.suffixSpacing = 2.0
return customTextField
}()
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .darkGray
topLabel.numberOfLines = 1
topLabel.backgroundColor = .red
topLabel.textAlignment = .center
return topLabel
}()
override public init(frame: CGRect) {
super.init(frame: frame)
setupBorders()
}
public override func layoutSubviews() {
setupViews()
}
func setupBorders() {
self.layer.cornerRadius = 10.0
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.borderWidth = 1.0
}
func setupViews() {
addSubview(topLabel)
// insertSubview(topLabel, aboveSubview: customTextField)
insertSubview(customTextField, belowSubview: topLabel)
customTextField.topAnchor.constraint(equalTo: topAnchor).isActive = true
customTextField.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
customTextField.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
customTextField.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
topLabel.centerYAnchor.constraint(equalTo: topAnchor).isActive = true
topLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
topLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
topLabel.text = labelText
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
}
}
You can try to organize it by creating a UIView subclass , so everything appear properly in it's order of adding
class CustomView: UIView {
var priceTextField: CustomTextField = {
let priceTextField = CustomTextField()
priceTextField.layer.cornerRadius = 10.0
priceTextField.layer.borderWidth = 1.0
priceTextField.layer.borderColor = UIColor.darkGray.cgColor
priceTextField.translatesAutoresizingMaskIntoConstraints = false
priceTextField.font = UIFont.systemFont(ofSize: 15)
priceTextField.textColor = .black
priceTextField.text = "0"
priceTextField.suffix = "EUR"
priceTextField.suffixTextColor = .darkGray
priceTextField.suffixSpacing = 2.0
priceTextField.textAlignment = .center
priceTextField.labelText = "Price"
return priceTextField
}()
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.textAlignment = .center
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .lightGray
topLabel.backgroundColor = .white
topLabel.numberOfLines = 1
return topLabel
}()
var lableStr:String?
init(frame: CGRect,lblTex:String) {
super.init(frame: frame)
lableStr = lblTex
createSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
createSubviews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
createSubviews()
}
func createSubviews() {
// all the layout code from above
// add the textfield then the label and set constraints properly
}
}
According to the Apple specification: It is composited above the receiver’s contents and sublayers.
So, the border will always be above all subviews, even if one brings the subview to the front and so on.
So one needs to make a background view to fake the border.
similar to Stackoverflow Question
Example:
Here self is "TextField"
activeborderView is "UiView"
activeborderView.frame = CGRect.init(x: -1, y: -1, width: self.frame.size.width+2, height: self.frame.size.height+2)
activeborderView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(activeborderView)
activeborderView.topAnchor.constraint(equalTo: self.topAnchor, constant:-1).isActive = true // Place our label 10 pts above the text field
activeborderView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: -1).isActive=true
activeborderView.heightAnchor.constraint(equalToConstant: self.frame.size.height+2).isActive=true
activeborderView.widthAnchor.constraint(equalToConstant: self.frame.size.width+2).isActive=true
activeborderView.layer.borderWidth = 3
activeborderView.layer.borderColor = CustomColor.blue().cgColor
activeborderView.layer.cornerRadius = 5
activeborderView.backgroundColor = .white
self.sendSubviewToBack(activeborderView)
self.setNeedsDisplay()

Mask UIView with another UIView

Yes this question has been asked before, the solutions did not work or had different applications.
It is the most basic setup. I have two rectangular UIViews, red and blue.
I would like the blue square to cut into the red square, so the red square looks like an "L"
import Foundation
import UIKit
class TestController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = .gray
view.addSubview(viewA)
view.addSubview(maskView)
viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewA.translatesAutoresizingMaskIntoConstraints = false
maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
maskView.translatesAutoresizingMaskIntoConstraints = false
// Things which don't work
//viewA.mask = maskView // both views disappear
//viewA.layer.mask = maskView.layer // both views disappear
//viewA.layer.addSublayer(maskView.layer) // hides mask view
}
var viewA: UIView = {
let view = UIView()
view.backgroundColor = .red
view.layer.masksToBounds = true
return view
}()
var maskView: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
}
This is the result I am expecting: (done in Photoshop)
As there is no magic way to mask the way in iOS, I present here a simple way to achieve this.
Don't forget to pan the clear area, If leaving the red square, it will become a blue square.
It's not hard to modify the subclass of UIViews for your own purpose, especially views.
import UIKit
class TestController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = .gray
view.addSubview(viewA)
view.addSubview(maskView)
maskView.maskedView = viewA
viewA.activeMask = maskView
viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewA.translatesAutoresizingMaskIntoConstraints = false
maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
maskView.translatesAutoresizingMaskIntoConstraints = false
}
var viewA: MyUIView = {
let view = MyUIView()
view.backgroundColor = .clear
view.layer.masksToBounds = true
return view
}()
var maskView: ActiveMaskView = {
let view = ActiveMaskView()
view.backgroundColor = .clear
return view
}()
}
class ActiveMaskView: UIView{
override func didMoveToSuperview() {
super.didMoveToSuperview()
let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(moveAround(_:)))
self.addGestureRecognizer(panGesture)
}
weak var maskedView : UIView?
private var frameOrigin : CGPoint = CGPoint.zero
#objc func moveAround(_ panGesture: UIPanGestureRecognizer){
guard let superview = superview else {return}
switch panGesture.state {
case .began:
frameOrigin = frame.origin
self.backgroundColor = UIColor.blue
case .changed:
let translation = panGesture.translation(in: superview)
frame = CGRect.init(origin: CGPoint.init(x: frameOrigin.x + translation.x, y: frameOrigin.y + translation.y), size: frame.size)
maskedView?.setNeedsDisplay()
break
case .ended:
self.backgroundColor =
frame.intersects(maskedView!.frame) ?
UIColor.clear : UIColor.blue
maskedView?.setNeedsDisplay()
case .cancelled:
frame = CGRect.init(origin: frameOrigin , size: frame.size)
self.backgroundColor =
frame.intersects(maskedView!.frame) ?
UIColor.clear : UIColor.blue
maskedView?.setNeedsDisplay()
default:
break;
}
}
}
class MyUIView: UIView{
weak var activeMask: ActiveMaskView?
override func draw(_ rect: CGRect) {
super.draw(rect)
let ctx = UIGraphicsGetCurrentContext()
ctx?.setFillColor(UIColor.red.cgColor)
ctx?.fill(self.layer.bounds)
ctx?.setBlendMode(.sourceOut)
guard let activeMask = activeMask , let superview = superview else {
return
}
let sc = frame.intersection(activeMask.frame)
let interSection = superview.convert(sc, to: self)
ctx?.fill(interSection )
}
}

Swift: How to resize the font size within a UITextView while using AutoLayout?

I am one week into Swift programing and I want to build my first Application with Autolayout.
The current state of my app is that I generate a bunch of PictureCell in my ViewController. Their size is based on a slider value (and also calculated in the ViewController). This works just fine.
My struggle is customizing the inside of my PictureCell. My goal is to have a Label in the cell which font size is automatically resized when I resize the cell.
At the current state I can resize the Cell and the UITextView like I want, but I cannot resize the font within the Textview because it's constant is just called when it is initialized (I guess).
How can I address this problem in a good way?
Due to a not understanding of Swifts logic I have to post the whole code of the PictureCell:
class PictureCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.layer.cornerRadius = self.bounds.width / 20
self.clipsToBounds = true
setupViews()
}
let descriptionTextView: UITextView = {
let textView = UITextView()
textView.text = "Header"
textView.textColor = .black
textView.backgroundColor = .white
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textAlignment = .center
textView.isEditable = false
textView.isScrollEnabled = false
textView.sizeToFit()
textView.font = UIFont.boldSystemFont(ofSize: textView.contentSize.height / 2) // Resize that
textView.layer.borderWidth = 2
textView.layer.borderColor = UIColor.red.cgColor
return textView
}()
var mainPicture: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.clipsToBounds = true
return imageView
}()
func setPictureForIndex(index: Int) {
self.mainPicture.image = UIImage(named: "color\(index)")
}
func setupViews() {
addSubview(mainPicture)
confMainPicture()
addSubview(descriptionTextView)
confDescriptionTextView()
}
func confMainPicture() {
mainPicture.translatesAutoresizingMaskIntoConstraints = false
mainPicture.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
mainPicture.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
mainPicture.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
mainPicture.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
func confDescriptionTextView(){
descriptionTextView.translatesAutoresizingMaskIntoConstraints = false
descriptionTextView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
descriptionTextView.heightAnchor.constraint(equalTo: mainPicture.heightAnchor, multiplier: 0.25).isActive = true
descriptionTextView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
}
Too small for the text
Just fine
Too big to look good
This Code solved my problem more or less:
It doesn't work properly if the Cell is really small but it's better than the starting point and maybe someone can use it.
class PictureCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.layer.cornerRadius = self.bounds.width / 20
self.clipsToBounds = true
setupViews()
}
//MARK: -
var cellIdetifier = Int()
var mainPicture: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.clipsToBounds = true
imageView.layer.cornerRadius = imageView.bounds.width / 20
return imageView
}()
var descriptionBox: UIView = {
let descVie = UIView()
descVie.backgroundColor = UIColor(red: 0.1 , green: 0.1, blue: 0.1, alpha: 0.5)
descVie.layer.borderWidth = 0.5
descVie.layer.borderColor = UIColor.black.cgColor
descVie.clipsToBounds = true
descVie.layer.cornerRadius = descVie.bounds.height / 5
return descVie
}()
lazy var descLabel: UITextField = {
let label = UITextField()
label.textColor = .white
label.textAlignment = .center
label.clipsToBounds = true
label.font = UIFont.systemFont(ofSize: 15)
label.adjustsFontSizeToFitWidth = true
label.autoresizingMask = [.flexibleWidth,.flexibleHeight]
label.sizeToFit()
label.layoutIfNeeded()
label.isUserInteractionEnabled = false
return label
}()
func setPictureForIndex(index: Int, name: String) {
self.descLabel.text = name
self.mainPicture.image = UIImage(named: "color\(index)")
}
// MARK: -
// MARK: Layout
func setupViews() {
addSubview(mainPicture)
addSubview(descriptionBox)
descriptionBox.addSubview(descLabel)
confBounds()
}
func confBounds() {
mainPicture.translatesAutoresizingMaskIntoConstraints = false
mainPicture.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
mainPicture.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
mainPicture.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
mainPicture.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
descriptionBox.translatesAutoresizingMaskIntoConstraints = false
descriptionBox.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
descriptionBox.heightAnchor.constraint(equalTo: mainPicture.heightAnchor, multiplier: 0.25).isActive = true
descriptionBox.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
descriptionBox.bottomAnchor.constraint(greaterThanOrEqualTo: mainPicture.topAnchor, constant: 1)
descLabel.translatesAutoresizingMaskIntoConstraints = false
descLabel.widthAnchor.constraint(equalTo: descriptionBox.widthAnchor).isActive = true
descLabel.heightAnchor.constraint(equalTo: descriptionBox.heightAnchor).isActive = true
descLabel.bottomAnchor.constraint(equalTo: descriptionBox.bottomAnchor).isActive = true
descLabel.heightAnchor.constraint(equalTo: descriptionBox.heightAnchor).isActive = true
}
}

UIScrollViews and anchors swift

I am having trouble setting up a scroll view and actually scrolling down. I populated the scroll view with some textfields and used anchors (topanchor,leftanchor...) to position them inside the scroll view. Even if I set the scroll view height to 1000, it wont actually move, it continues to show the same items, the scroll indicator does go down but the content itself doesnt, I already set the scroll view to scrollenabled, and delegate to self.
I think the problem might be with the anchors but then how will I arrange my items inside the scroll view, any sugestion will be greatly appreaciated.
EDIT : The code below indicates the anchors applied to the scroll view ( inputContainer ), the img corresponds to an UIImageView and the mainContainer to the UIView containing the img and the inputContainer.
inputContainer.topAnchor.constraintEqualToAnchor( img.bottomAnchor ).active = true
inputContainer.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
inputContainer.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor ).active = true
inputContainerBottomConstraint = inputContainer.bottomAnchor.constraintEqualToAnchor( cancelButton.topAnchor )
inputContainerBottomConstraint?.active = true
EDIT: This is how the code looks like :
class SView : UIView, UITextFieldDelegate, UIScrollViewDelegate {
let mainContainer : UIView = {
let v = UIView()
v.backgroundColor = .whiteColor()
v.layer.cornerRadius = 8
v.layer.masksToBounds = true
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let Img : UIImageView = {
let img = UIImageView()
img.image = UIImage(named: "noImage")
img.backgroundColor = .blueColor()
img.translatesAutoresizingMaskIntoConstraints = false
img.contentMode = .ScaleAspectFill
img.clipsToBounds = true
return img
}()
let inputContainer : UIScrollView = {
let ic = UIScrollView()
ic.backgroundColor = .whiteColor()
ic.translatesAutoresizingMaskIntoConstraints = false
return ic
}()
let datePickerTextField : UITextField = {
let tf = UITextField()
tf.placeholder = "Fecha"
tf.textAlignment = .Center
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
let tagsTextField : UITextField = {
let tf = UITextField()
tf.placeholder = "Tags"
tf.textAlignment = .Center
tf.clearButtonMode = .Always
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
lazy var cancelButton : UIButton = {
let button = UIButton()
button.backgroundColor = UIColor.rgb(255, green: 65, blue: 65, alpha: 1)
button.setTitle("Cancelar", forState: .Normal)
button.tintColor = .whiteColor()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget( self , action: #selector(handleCancelButtonPressed), forControlEvents: .TouchUpInside)
return button
}()
lazy var publicarButton : UIButton = {
let button = UIButton()
button.backgroundColor = UIColor.rgb(0 , green: 204, blue: 102, alpha: 1)
button.setTitle("Publicar", forState: .Normal)
button.tintColor = .whiteColor()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget( self , action: #selector(handlePublicarButtonPressed), forControlEvents: .TouchUpInside)
return button
}()
override init(frame: CGRect)
{
super.init(frame: frame)
inputContainer.delegate = self
datePickerTextField.delegate = self
tagsTextField.delegate = self
setupMainContainer()
setupImg()
setupButtons()
setupInputContainer()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupMainContainer ()
{
addSubview(mainContainer)
mainContainer.centerXAnchor.constraintEqualToAnchor( centerXAnchor ).active = true
mainContainer.centerYAnchor.constraintEqualToAnchor( centerYAnchor ).active = true
mainContainer.widthAnchor.constraintEqualToAnchor( widthAnchor ).active = true
mainContainer.heightAnchor.constraintEqualToAnchor( heightAnchor )
}
func setupImg ()
{
mainContainer.addSubview(Img)
Img.topAnchor.constraintEqualToAnchor( mainContainer.topAnchor ).active = true
Img.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
Img.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor ).active = true
Img.heightAnchor.constraintEqualToAnchor( mainContainer.heightAnchor , multiplier: 0.3).active = true
}
var inputContainerBottomConstraint : NSLayoutConstraint?
func setupInputContainer ()
{
mainContainer.addSubview(inputContainer)
inputContainer.topAnchor.constraintEqualToAnchor( Img.bottomAnchor ).active = true
inputContainer.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
inputContainer.rightAnchor.constraintEqualToAnchor( mainContainer.rightAnchor ).active = true
inputContainerBottomConstraint = inputContainer.bottomAnchor.constraintEqualToAnchor( cancelButton.topAnchor )
inputContainerBottomConstraint?.active = true
inputContainer.addSubview( datePickerTextField )
inputContainer.addSubview( tagsTextField )
datePickerTextField.topAnchor.constraintEqualToAnchor( inputContainer.topAnchor ).active = true
datePickerTextField.centerXAnchor.constraintEqualToAnchor( inputContainer.centerXAnchor ).active = true
datePickerTextField.widthAnchor.constraintEqualToAnchor( inputContainer.widthAnchor ).active = true
datePickerTextField.heightAnchor.constraintEqualToAnchor( inputContainer.heightAnchor, multiplier: 0.2 ).active = true
tagsTextField.bottomAnchor.constraintEqualToAnchor( inputContainer.bottomAnchor ).active = true
tagsTextField.centerXAnchor.constraintEqualToAnchor( inputContainer.centerXAnchor ).active = true
tagsTextField.widthAnchor.constraintEqualToAnchor( inputContainer.widthAnchor ).active = true
tagsTextField.heightAnchor.constraintEqualToAnchor( inputContainer.heightAnchor, multiplier: 0.2 ).active = true
}
func setupButtons()
{
mainContainer.addSubview( cancelButton )
mainContainer.addSubview( publicarButton )
cancelButton.bottomAnchor.constraintEqualToAnchor( mainContainer.bottomAnchor).active = true
cancelButton.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
cancelButton.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor, multiplier: 0.5 ).active = true
cancelButton.heightAnchor.constraintEqualToAnchor( mainContainer.heightAnchor, multiplier: 0.1).active = true
publicarButton.bottomAnchor.constraintEqualToAnchor( mainContainer.bottomAnchor).active = true
publicarButton.leftAnchor.constraintEqualToAnchor( cancelButton.rightAnchor ).active = true
publicarButton.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor, multiplier: 0.5 ).active = true
publicarButton.heightAnchor.constraintEqualToAnchor( mainContainer.heightAnchor, multiplier: 0.1).active = true
} }
So when the keyboard appears the bottom anchor constant of the scroll view changes so that the keyboard "top anchor" is the new bottom anchor.
With the constraints you have described there's no way for the layout engine to determine the content height of the scroll view. You should pin your bottom text field to the bottom of the scroll view. This way the scroll view's content size will resize up to the max y of all of the text fields. Here is some code you can put in a playground to see:
import UIKit
import XCPlayground
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 200, height: 150))
scrollView.backgroundColor = UIColor.whiteColor()
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.backgroundColor = UIColor.purpleColor()
let otherTextField = UITextField()
otherTextField.translatesAutoresizingMaskIntoConstraints = false
otherTextField.backgroundColor = UIColor.purpleColor()
let otherOtherTextField = UITextField()
otherOtherTextField.translatesAutoresizingMaskIntoConstraints = false
otherOtherTextField.backgroundColor = UIColor.purpleColor()
scrollView.addSubview(textField)
textField.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
textField.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
textField.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
scrollView.addSubview(otherTextField)
otherTextField.topAnchor.constraintEqualToAnchor(textField.bottomAnchor, constant: 60).active = true
otherTextField.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
otherTextField.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
scrollView.addSubview(otherOtherTextField)
otherOtherTextField.topAnchor.constraintEqualToAnchor(otherTextField.bottomAnchor, constant: 60).active = true
otherOtherTextField.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
otherOtherTextField.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
otherOtherTextField.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
scrollView.setNeedsLayout()
XCPlaygroundPage.currentPage.liveView = scrollView
This places three text fields in a scroll view with 60 points between the center and top and bottom. If you comment out:
otherOtherTextField.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
The scroll view in the assistant editor will not scroll, but with it it does.

Resources