View not displaying and centralising with autolayout - ios

I want to add a view in my view controller programmatically and centralise it. I added the view as subview to the parent view and enable autolayout yet its not showing.
import UIKit
class ViewController: UIViewController {
lazy var newView:UIView = {
let view = UIView()
view.backgroundColor = #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)
view.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
return view
}()
let titleLable = UILabel()
let bodyLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
newView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
newView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
}

Try this way
NSLayoutConstraint(item: newView,
attribute: NSLayoutConstraint.Attribute.centerX,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: view,
attribute: NSLayoutConstraint.Attribute.centerX,
multiplier: 1,
constant: 0).isActive = true
NSLayoutConstraint(item: newView,
attribute: NSLayoutConstraint.Attribute.centerY,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: view,
attribute: NSLayoutConstraint.Attribute.centerY,
multiplier: 1,
constant: 0).isActive = true

Size constraints are missing for newView. You removed them with newView.translatesAutoresizingMaskIntoConstraints = false. To restore all required constraints add the lines below to viewDidLoad:
newView.widthAnchor.constraint(equalToConstant: 200).isActive = true
newView.heightAnchor.constraint(equalToConstant: 200).isActive = true

here is the problem...
first, you set the frame of newView,
second, you also set translatesAutoresizingMaskIntoConstraints = false
so Xcode does not understand whats you want to set frame or constraints.
so if you set frame then you cannot add constraints again on it later.
so the best solution is here according to your need is:
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
newView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
newView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
newView.widthAnchor.constraint(equalToConstant: 200).isActive = true
newView.heightAnchor.constraint(equalToConstant: 200).isActive = true

Related

How can I add text in the center of a UIView Programmatically?

I have the following UIView, how can text be added in the center of this UIView programmatically?
This is the UIView code:
let newView = UIView()
newView.backgroundColor = .groupTableViewBackground
self.view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
let guide = self.view.safeAreaLayoutGuide
newView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
newView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
newView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
newView.heightAnchor.constraint(equalToConstant: 50).isActive = true
} else {
NSLayoutConstraint(item: newView,
attribute: .top,
relatedBy: .equal,
toItem: view, attribute: .top,
multiplier: 1.0, constant: 0).isActive = true
NSLayoutConstraint(item: newView,
attribute: .leading,
relatedBy: .equal, toItem: view,
attribute: .leading,
multiplier: 1.0,
constant: 0).isActive = true
NSLayoutConstraint(item: newView, attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailing,
multiplier: 1.0,
constant: 0).isActive = true
newView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
This was my attempt at adding text to the center of the UIView programmatically, but I'm not sure why it is not working?
let lb = UILabel()
lb.centerXAnchor.constraint(equalTo: newView.centerXAnchor).isActive = true
lb.centerYAnchor.constraint(equalTo: newView.centerYAnchor).isActive = true
lb.text="anything"
newView.backgroundColor = UIColor.white
// show on screen
self.view.addSubview(newView)
newView.addSubview(lb)
lb.center = newView.center
UPDATE:
How can this button border below the UIView that's under the navigation controller be added?
When it comes to adding constraints to the view programmatically, note that you have to set translatesAutoresizingMaskIntoConstraints to false (but I get the feeling that you already know this, and just forgot to add it to the label)
let lb = UILabel()
lb.textAlignment = .center
lb.numberOfLines = 0
newView.addSubview(lb)
lb.translatesAutoresizingMaskIntoConstraints = false
lb.centerXAnchor.constraint(equalTo: newView.centerXAnchor).isActive = true
lb.centerYAnchor.constraint(equalTo: newView.centerYAnchor).isActive = true
Add border (assuming you want to add the border to the newView):
let border = UIView()
newView.addSubview(border)
border.translatesAutoresizingMaskIntoConstraints = false
border.leadingAnchor.constraint(equalTo: newView.leadingAnchor).isActive = true
border.trailingAnchor.constraint(equalTo: newView.trailingAnchor).isActive = true
border.bottomAnchor.constraint(equalTo: newView.bottomAnchor).isActive = true
border.heightAnchor.constraint(equalToConstant: 1).isActive = true
But for the border it would probably be cleaner to write an extension on UIView, so the code can be reusable in other places, if you ever need it again

Swift: Stacking subviews dynamically inside a superview

I am trying to create a login in page that is supposed to look like the one listed below. The code I have written should produce this view minus the logo and minus [Login|Register] toggle button. The height and width of my box is also different, but I'm not concerned about that.
Currently I am getting this output. I'm concerned about how the words are all overlapping each other at the top.
In the code I listed below I create my 3 text fields, my button and the container for my text fields. I believe there is something wrong in the function fieldConstraints. In this function I look through an array of all my text fields and assign the neccessary constraints to them. They all get the same constraints except that the topAnchor of each text field after the first one is set equal to the bottomAnchor of the separator that was placed underneath the text field before. Those blue lines in between the text fields are the separators.
Main class
class SignIn: UIViewController {
override func loadView() {
super.loadView()
let inputContainer = inputDataContainer()
constraintsToCenterSubview(forView: inputContainer, width: 100, height: 100)
let nameField = field(for: "Name")
let emailField = field(for: "Email address")
let passField = field(for: "Password")
let fields = [nameField, emailField, passField]
let button = loginButton()
fieldConstraints(subviews: fields, superview: inputContainer)
self.centerViewBelow(forView: button, whichIsBelow: inputContainer, increaseWidthBy: 0)
}
func inputDataContainer() -> UIView{
let inputView = UIView(frame: CGRect(x: self.view.center.x, y: self.view.center.y, width: CGFloat(100), height: CGFloat(100)))
inputView.backgroundColor = UIColor.white
inputView.translatesAutoresizingMaskIntoConstraints = false
inputView.layer.cornerRadius = 5
inputView.layer.masksToBounds = true
self.view.addSubview(inputView)
//inputView = centerViewBelow(forView: inputView, whichIsBelow: self.view, increaseWidthBy: 100)
return inputView
}
func loginButton() -> UIButton {
let button = UIButton()
button.backgroundColor = UIColor(r: 80, g: 101, b: 161)
button.setTitle("Submit", for: [])
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(UIColor.white, for: [])
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
self.view.addSubview(button)
return button
}
func field(for name: String) -> UITextField{
let tf = UITextField()
tf.placeholder = name
tf.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(tf)
return tf
}
func fieldSep() -> UIView {
let view = UIView()
view.backgroundColor = UIColor(r: 220, g: 220, b: 220)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}
func fieldConstraints(subviews: [UIView], superview: UIView) {
var sep: UIView?
let len = subviews.endIndex
for (idx, subview) in subviews.enumerated(){
superview.addSubview(subview)
subview.leftAnchor.constraint(equalTo: superview.leftAnchor)
subview.rightAnchor.constraint(equalTo: superview.rightAnchor)
subview.widthAnchor.constraint(equalTo: superview.widthAnchor)
subview.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: CGFloat(1/len))
if (sep != nil){
subview.topAnchor.constraint(equalTo: sep!.bottomAnchor)
}else{
subview.topAnchor.constraint(equalTo: superview.topAnchor)
}
sep = fieldSep()
if idx < subviews.endIndex-1 {
self.view.addSubview(sep!)
sep?.leftAnchor.constraint(equalTo: superview.leftAnchor)
sep?.rightAnchor.constraint(equalTo: superview.rightAnchor)
sep?.topAnchor.constraint(equalTo: subview.bottomAnchor)
}
}
}
}
Extensions
extension UIColor {
convenience init(r:CGFloat, g:CGFloat, b: CGFloat) {
self.init(red: r/255, green: g/255, blue: b/255, alpha: 1)
}
}
extension UIViewController {
func centerViewBelow(forView view: UIView, whichIsBelow topView: UIView, increaseWidthBy constant: CGFloat){
let topConstraint = NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: topView, attribute: .bottom, multiplier: 1, constant: 20)
let widthConstraint = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: topView, attribute: .width, multiplier: 1, constant: constant)
let centerConstraint = NSLayoutConstraint(item: view, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
let heightConstraint = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 50)
NSLayoutConstraint.activate([topConstraint, widthConstraint, centerConstraint, heightConstraint])
//return view
}
func constraintsToCenterSubview(forView view: UIView, width: Int, height: Int){
let centerXConstraint = NSLayoutConstraint(item: view, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
let centerYConstraint = NSLayoutConstraint(item: view, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: 0)
let widthConstraint = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: CGFloat(width))
let heightConstraint = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: CGFloat(height))
NSLayoutConstraint.activate([centerXConstraint, centerYConstraint, widthConstraint, heightConstraint])
}
}
Thank you
----------------------------------------------------------------------------------------------------------
Update
So I was pretty much able to do it by changing my container into a stack view. But this made it so that my corners were no longer rounded. Does anyone know how to fix this?
func inputDataContainer() -> UIStackView{
let inputView = UIStackView(frame: CGRect(x: self.view.center.x, y: self.view.center.y, width: CGFloat(100), height: CGFloat(100)))
inputView.backgroundColor = UIColor.white
inputView.translatesAutoresizingMaskIntoConstraints = false
inputView.layer.cornerRadius = 5
inputView.layer.masksToBounds = true
inputView.distribution = .fillEqually
inputView.axis = .vertical
inputView.spacing = 1
self.view.addSubview(inputView)
//inputView = centerViewBelow(forView: inputView, whichIsBelow: self.view, increaseWidthBy: 100)
return inputView
}
func fieldConstraints(subviews: [UIView], superview: UIStackView) {
for subview in subviews{
superview.addArrangedSubview(subview)
subview.clipsToBounds = true
}
}
Example Screen Shot of Current App
Try to give height for
sep?.heightAnchor.constraint(equalToConstant:1.0).isActive = true
also for all constraints in fieldConstraints you forget
.isActive = true
or use NSLayoutConstraint.activate , like
NSLayoutConstraint.activate([
subview.leftAnchor.constraint(equalTo: superview.leftAnchor),
subview.rightAnchor.constraint(equalTo: superview.rightAnchor)
subview.widthAnchor.constraint(equalTo: superview.widthAnchor),
subview.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: CGFloat(1/len))
])
This approach will work but it will be better to use a vertical stackview with distribution .fillEqually and it'll will partition them and add like
fileds.forEach { stackview.addArrangedSubview($0) }
use UIStackView. it will help you save time to build a complicated UI.
You will have a little trouble using a UIStackView. You could embed it in a UIView and give that view the rounded corners, but then you have to add padding to the fields within the stack view in order to get your separator lines.
Here is another approach that might work better for you. It's closer to your original code, adding the fields and separator views to a UIView:
extension UIColor {
convenience init(r:CGFloat, g:CGFloat, b: CGFloat) {
self.init(red: r/255, green: g/255, blue: b/255, alpha: 1)
}
}
class SampleViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(r: 65, g: 92, b: 144)
let nameField = field(for: "Name")
let sep1 = fieldSep()
let emailField = field(for: "Email address")
let sep2 = fieldSep()
let passField = field(for: "Password")
let views = [nameField, sep1, emailField, sep2, passField]
let inputView = inputDataContainer(with: views)
view.addSubview(inputView)
NSLayoutConstraint.activate([
inputView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
inputView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0),
inputView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0),
])
}
func field(for s: String) -> UITextField {
let f = UITextField()
f.translatesAutoresizingMaskIntoConstraints = false
f.placeholder = s
f.borderStyle = .none
return f
}
func fieldSep() -> UIView {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
v.backgroundColor = UIColor(r: 220, g: 220, b: 220)
return v
}
func inputDataContainer(with subviews: [UIView]) -> UIView {
let horizontalPadding: CGFloat = 8.0
let verticalSpacing: CGFloat = 8.0
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = .white
containerView.layer.cornerRadius = 5
var previousView: UIView?
for subview in subviews{
containerView.addSubview(subview)
// if it's a text field, we want padding on left and right
if subview is UITextField {
subview.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: horizontalPadding).isActive = true
subview.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -horizontalPadding).isActive = true
} else {
subview.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 0.0).isActive = true
subview.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0.0).isActive = true
}
if subview == subviews.first {
// if it's the first subview, constrain to top of container
subview.topAnchor.constraint(equalTo: containerView.topAnchor, constant: verticalSpacing).isActive = true
} else {
// unwrap previousView and constrain subview to bottom of previous subview
if let pv = previousView {
subview.topAnchor.constraint(equalTo: pv.bottomAnchor, constant: verticalSpacing).isActive = true
}
}
if subview == subviews.last {
// if it's the last subview, constrain to bottom of container
subview.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -verticalSpacing).isActive = true
}
// save reference to current subview
previousView = subview
}
return containerView
}
}
And the result:

I'm currently having trouble updating constraints for a UIVIew in Autolayout

I've created a custom View that has subviews added to it and a UIPanGestureRecognizer is added to each UIView object but when I select the UIView after I update the constraints there is a delay. If I drag the UIView (sView) off the screen, the next time I drag the view in either direction there is a delay of a few seconds before the pan gesture recognizer registers. It doesn't register the pan gesture immediately.
I've read up on auto layout and believe its configured correctly in the animation callback method. I've created a delegate in the view controller that has access to the updated constraints, and sView.leftConstraint and sView.rightConstraint are fields I have added to a custom view that represents a draggable UIView.
The ScrollViewController adopts a protocol so that it can access the view that was selected and update the constraints accordingly.
Any help would be greatly appreciated :)
extension ScrollViewController: DragDelegate {
func draggedLeft(sView: DraggableView) {
print("Dragged Left")
confirmationLabel.text = ""
sView.selectionLabel.text = "Not Selected"
sView.cornerRadius = 7
sView.rightConstraint?.isActive = false
sView.leftConstraint?.isActive = false
sView.leftConstraint?.constant = 1
sView.leftConstraint?.isActive = true
// sView.widthConstraint?.constant = 300
UIView.animate(withDuration: 0.3) {
sView.updateConstraints()
self.contentView.setNeedsLayout()
self.view.layoutIfNeeded()
}
}
}
class OrderSelectionView: UIView {
var dView : DraggableView?
var dView2 : DraggableView?
let screenWidth = UIScreen.main.bounds.width
//I need to set up all the constraints in this class
//This will act almost like a container view
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
let margins = self.layoutMarginsGuide
self.translatesAutoresizingMaskIntoConstraints = false
dView = DraggableView(frame: frame)
dView2 = DraggableView(frame: frame)
guard let dragView = dView else {return}
guard let dragView2 = dView2 else {return}
dragView.translatesAutoresizingMaskIntoConstraints = false
dragView.topAnchor.constraint(equalTo: laundryLabel.bottomAnchor, constant: 10).isActive = true
let rightAnchor = NSLayoutConstraint(item: dragView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0)
let leftAnchor = NSLayoutConstraint(item: dragView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0)
dragView.widthAnchor.constraint(equalToConstant: 150).isActive = true
dragView.heightAnchor.constraint(equalToConstant: 40).isActive = true
dragView.leftConstraint = leftAnchor
dragView.leftConstraint?.isActive = true
dragView.rightConstraint = rightAnchor
self.addSubview(dragView2)
dragView2.translatesAutoresizingMaskIntoConstraints = false
let laundryLabel2 = createLaundryLabel(lTitle: "Dry", constant: 100)
dragView2.translatesAutoresizingMaskIntoConstraints = false
dragView2.topAnchor.constraint(equalTo: laundryLabel2.bottomAnchor, constant: 10).isActive = true
let rightAnchor2 = NSLayoutConstraint(item: dragView2, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: -50)
let leftAnchor2 = NSLayoutConstraint(item: dragView2, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0)
dragView2.widthAnchor.constraint(equalToConstant: 150).isActive = true
dragView2.heightAnchor.constraint(equalToConstant: 40).isActive = true
dragView2.leftConstraint = leftAnchor2
dragView2.leftConstraint?.isActive = true
dragView2.rightConstraint = rightAnchor2
}

UIView not showing in UIViewcollection in Swift, Programmatically

the below code is the viewController that is navigated with a navigationItem navigationItem.leftBarButtonItem = UIBarButtonItem(title: from a previous tableViewController and I am trying to produce a UIView in my viewController. However nothing is showing.
import UIKit
class SecondpgController: UIViewController {
var inputContainerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor .gray
let inputContainerView = UIView()
self.view.addSubview(inputContainerView)
//inputContainerView.backgroundColor = UIColor(red: 162/255, green: 20/255, blue: 35/255, alpha: 1)
inputContainerView.backgroundColor = .white
inputContainerView.translatesAutoresizingMaskIntoConstraints = false
//inputContainerView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 300).isActive = true
//nputContainerView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10).isActive = true
inputContainerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 200).isActive = true
inputContainerView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 200).isActive = true
The app is running well however after reaching the view controller, Nothing is shown but only a grey screen.
Why is this happening and how can I solve it?
Your problem is that you constraints are only setting x and y position for the UIView you are creating. You are missing width and height.
Try this constraints:
inputContainerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
inputContainerView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
inputContainerView.widthAnchor.constraint(equalToConstant: 100).isActive = true
inputContainerView.heightAnchor.constraint(equalToConstant: 100).isActive = true
This code means that your UIView will be centred vertically and horizontally in your self.view and also it has a width and height of 100. It will look like this:
Hope it helps. Happy Coding
You have added constraints to place inputContainerView to the centre of the screen but did not add constraints for it's height and width. Adding the following code might solve your issue:
inputContainerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0).isActive = true
inputContainerView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0).isActive = true
inputContainerView.addConstraint(NSLayoutConstraint(item: inputContainerView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200))
inputContainerView.addConstraint(NSLayoutConstraint(item: inputContainerView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200))
Check this: https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html
Ensure that the constants for CenterXAnchor and CenterYAnchor are 0, so the view aligns at the centre. And make sure you add inputContainerView only once either as an outlet or programmatically.

Programmatically creating layout, using stack view and constraints not working

I'm trying to create this layout programmatically in Swift.
https://codepen.io/anon/pen/NXRQbJ
The first rectangle is small. And the other 2 rectangles are the same size but bigger than the first rectangle.
Here is the entire View Controller. I am not using storyboard. All my code is in this view controller.
import UIKit
class ViewController: UIViewController {
lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [smallRectangleView, bigRectangleView, bigRectangleView2])
stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
var smallRectangleView: UIView = {
let view = UIView()
view.backgroundColor = .orange
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var bigRectangleView: UIView = {
let view = UIView()
view.backgroundColor = .green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var bigRectangleView2: UIView = {
let view = UIView()
view.backgroundColor = .purple
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
setupLayout()
print(stackView.frame)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupLayout() {
// Stack View
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
// Small Recntangle View
let heightConstraint1 = NSLayoutConstraint(item: smallRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView, attribute: .height, multiplier: 4.0, constant: 0.0)
// Big Rectangle View
let heightConstraint2 = NSLayoutConstraint(item: bigRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView2, attribute: .height, multiplier: 0.0, constant: 0.0)
view.addConstraints([heightConstraint1, heightConstraint2])
}
}
heightConstraint1 and heightConstraint2 is causing errors. I don't know why.
// Small Recntangle View
let heightConstraint1 = NSLayoutConstraint(item: smallRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView, attribute: .height, multiplier: 4.0, constant: 0.0)
// Big Rectangle View
let heightConstraint2 = NSLayoutConstraint(item: bigRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView2, attribute: .height, multiplier: 0.0, constant: 0.0)
view.addConstraints([heightConstraint1, heightConstraint2])
When I run this app on the simulator nothing shows up. It's just a blank white screen. Also in the debugger there are issues with constraints.
The main problem seems to be the stackView.distribution. I changed it from .fillProportionally to just .fill. I suspect the issue was that the views were already relative to each other because of your constraints and that lead to conflicts.
Other changes I made, your multiplier in one of your constraints needs to be 0.25 instead of 4, and in the second one the multiplier needs to be 1 instead of 0.
I also used NSLayoutConstraint.activate to activate the last 2 constraints instead of adding them to the view. In general, you want to activate constraints instead of adding them to views. Let iOS figure out which views to add them to.
class ViewController: UIViewController {
lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [smallRectangleView, bigRectangleView, bigRectangleView2])
stackView.alignment = .fill
stackView.distribution = .fill
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
var smallRectangleView: UIView = {
let view = UIView()
view.backgroundColor = .orange
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var bigRectangleView: UIView = {
let view = UIView()
view.backgroundColor = .green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var bigRectangleView2: UIView = {
let view = UIView()
view.backgroundColor = .purple
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
setupLayout()
print(stackView.frame)
}
func setupLayout() {
// Stack View
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
// Small Recntangle View
let heightConstraint1 = NSLayoutConstraint(item: smallRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView, attribute: .height, multiplier: 0.25, constant: 0.0)
// Big Rectangle View
let heightConstraint2 = NSLayoutConstraint(item: bigRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView2, attribute: .height, multiplier: 1.0, constant: 0.0)
NSLayoutConstraint.activate([heightConstraint1, heightConstraint2])
}
}
You can also continue to use iOS 9+ constraints instead of the older NSLayoutConstraint(item: without changing your original implementation:
func setupLayout() {
let topViewHeight:CGFloat = 50.0
let screenheight = view.bounds.height
// Stack View
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
smallRectangleView.heightAnchor.constraint(equalToConstant: topViewHeight).isActive = true
bigRectangleView.heightAnchor.constraint(equalToConstant: (screenheight - topViewHeight) / 2).isActive = true
bigRectangleView2.heightAnchor.constraint(equalToConstant: (screenheight - topViewHeight) / 2).isActive = true
}

Resources