the view size gets smaller when the view is rotated - ios

I have a view. when it's rotated, the view size gets smaller. would you please take a look at the pics that I've attached?
#IBAction func buttonForNewView(_ sender: Any) {
self.view.addSubview(customvView)
customvView.layer.cornerRadius = 15
customvView.backgroundColor = .gray
customvView.translatesAutoresizingMaskIntoConstraints = false
customvView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: view.frame.size.height*0.1).isActive = true
customvView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: view.frame.size.width*0.1).isActive = true
customvView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -view.frame.size.width*0.1).isActive = true
customvView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant:-view.frame.size.height*0.1).isActive = true
}

The constraints are not recalculated when the device is rotated.
This breaks out the constraint calculation into a separate function. Then it overrides traitCollectionDidChange(). When the trait collection changes, it removes the old constraints (to prevent auto layout from getting confused) and reapplies new constraints.
import UIKit
class ViewController: UIViewController {
var customvView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
customvView = UIView()
// Do any additional setup after loading the view.
}
func constrainCustomvView() {
customvView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: view.frame.size.height*0.1).isActive = true
customvView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: view.frame.size.width*0.1).isActive = true
customvView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -view.frame.size.width*0.1).isActive = true
customvView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant:-view.frame.size.height*0.1).isActive = true
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
if view.subviews.contains(customvView) { // for the case that the traits change before the view is created
// remove the existing constraits, or autolayout will have to choose between two and it will choose poorly
for constraint in view.constraints {
if let first = constraint.firstItem as? UIView, first == customvView {
view.removeConstraint(constraint)
}
if let second = constraint.secondItem as? UIView, second == customvView {
view.removeConstraint(constraint)
}
}
constrainCustomvView()
}
}
#IBAction func CustomButton(_ sender: Any) {
self.view.addSubview(customvView)
customvView.layer.cornerRadius = 15
customvView.backgroundColor = .gray
customvView.translatesAutoresizingMaskIntoConstraints = false
constrainCustomvView()
}
}

check this code.
let val = view.frame.size.height*0.1
self.view.addSubview(customvView)
customvView.layer.cornerRadius = 15
customvView.backgroundColor = .gray
customvView.translatesAutoresizingMaskIntoConstraints = false
customvView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: val).isActive = true
customvView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: val).isActive = true
customvView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -val).isActive = true
customvView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant:-val).isActive = true

Related

Dynamic Constraint for UIView & UICollection

On the top of my screen will show one of two UIViews.
One is the minimized version and the other is the maximized verison.
The minimized view is 30 in height while the maximized version is 250.
Underneath this there is a UICollectionView.
I want it so that when the minimized version of the UIView is showing, the UICollectionView's topAnchor will be connected to the UIViews bottomAnchor.
When I click on each of the UIViews they will become hidden and make the other one visible.
Here are some screenshots to help visualize:
Default
Minimized
Attempt to maximize
So when I show the maximized UIView I want the UICollectionViews topAnchor to be connected to that ones bottomAnchor and so forth.
Currently everything is working except the proper resizing of the UICollectionView.
It will resize up when I minimize, but will not resize down when I maximize.
My viewDidLoad calls both of these functions:
private func configureMaxView() {
view.addSubview(maxView)
maxView.layer.cornerRadius = 18
maxView.backgroundColor = .secondarySystemBackground
maxView.translatesAutoresizingMaskIntoConstraints = false
maxView.isHidden = isMaxViewHidden
let gesture = UITapGestureRecognizer(target: self, action: #selector (self.minimizeAction (_:)))
self.maxView.addGestureRecognizer(gesture)
let padding: CGFloat = 20
NSLayoutConstraint.activate([
maxView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
maxView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
maxView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
maxView.heightAnchor.constraint(equalToConstant: 250),
])
}
private func configureMinView() {
view.addSubview(minView)
minView.layer.cornerRadius = 9
minView.backgroundColor = .secondarySystemBackground
minView.translatesAutoresizingMaskIntoConstraints = false
minView.isHidden = !isMaxViewHidden
let gesture = UITapGestureRecognizer(target: self, action: #selector (self.expandAction (_:)))
self.minView.addGestureRecognizer(gesture)
let padding: CGFloat = 20
NSLayoutConstraint.activate([
minView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
minView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
minView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -padding),
minView.heightAnchor.constraint(equalToConstant: 30),
])
}
Here are the functions that are called when you click on one of the UIViews:
#objc func minimizeAction(_ sender:UITapGestureRecognizer){
minView.isHidden = false
maxView.isHidden = true
// without this call the hiding and unhiding works fine - But the collectionview won't move
resizeCollectionView()
isMaxViewHidden = !isMaxViewHidden
}
#objc func expandAction(_ sender:UITapGestureRecognizer){
minView.isHidden = true
maxView.isHidden = false
// without this call the hiding and unhiding works fine - But the collectionview won't move
resizeCollectionView()
isMaxViewHidden = !isMaxViewHidden
}
My viewDidLoad will also call this after setting up the uiviews in order to set up the collectionView:
private func configureCollectionView() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: UIHelper.createThreeColumnFlowLayout(in: view))
view.addSubview(collectionView)
collectionView.delegate = self
collectionView.backgroundColor = .systemBackground
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: CustomCell.resuseID)
collectionView.translatesAutoresizingMaskIntoConstraints = false
let bottomAnchor = isMaxViewHidden ? minView.bottomAnchor : maxView.bottomAnchor
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: bottomAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
Here is the function I call from each uiview click handler in an attempt to update the constraint:
private func resizeCollectionView() {
let bottomAnchor = isMaxViewHidden ? maxView.bottomAnchor: minView.bottomAnchor
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: bottomAnchor),
])
// This does move the collection view down, but seems hardcoded and bad
//collectionView.frame.origin.y = 650
}
Error:
"<NSLayoutConstraint:0x600000c4ed50 UIView:0x7ffd60c135e0.top == UILayoutGuide:0x6000016f89a0'UIViewSafeAreaLayoutGuide'.top (active)>",
"<NSLayoutConstraint:0x600000c4fa20 UIView:0x7ffd60c135e0.height == 250 (active)>",
"<NSLayoutConstraint:0x600000c4bed0 UIView:0x7ffd60c13750.top == UILayoutGuide:0x6000016f89a0'UIViewSafeAreaLayoutGuide'.top (active)>",
"<NSLayoutConstraint:0x600000c3c000 UIView:0x7ffd60c13750.height == 30 (active)>",
"<NSLayoutConstraint:0x600000c3dbd0 V:[UIView:0x7ffd60c135e0]-(0)-[UICollectionView:0x7ffd62819600] (active)>",
"<NSLayoutConstraint:0x600000c20cd0 V:[UIView:0x7ffd60c13750]-(0)-[UICollectionView:0x7ffd62819600] (active)>"
)
Will attempt to recover by breaking constraint
"<NSLayoutConstraint:0x600000c4ed50 UIView:0x7ffd60c135e0.top == UILayoutGuide:0x6000016f89a0'UIViewSafeAreaLayoutGuide'.top (active)>",
"<NSLayoutConstraint:0x600000c4fa20 UIView:0x7ffd60c135e0.height == 250 (active)>",
"<NSLayoutConstraint:0x600000c4bed0 UIView:0x7ffd60c13750.top == UILayoutGuide:0x6000016f89a0'UIViewSafeAreaLayoutGuide'.top (active)>",
"<NSLayoutConstraint:0x600000c3c000 UIView:0x7ffd60c13750.height == 30 (active)>",
What's happening in here is that, even though the view is being hidden, the constraints are still there, so assigning numerous top constraints on a single view will result to a conflict.
If you want to have a sort of dynamic constraint, just modify the constraint's constant or multiplier values. That way, you wont need to worry about constraint objects.
Wilson's solution is right. What I can just add from that is just have a single containerView containing your min and max views. and just toggle the visibility of your min and max view inside your containerView.
Here's my (simple) take on it:
everything is called in viewDidLoad() as you do, and
you also need to define a constraint object.
private var containerViewHeight: NSLayoutConstraint!
private func configureContainerView() {
containerView = UIView()
containerView.backgroundColor = .systemGreen
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(containerViewTapped)))
view.addSubview(containerView)
containerViewHeight = containerView.heightAnchor.constraint(equalToConstant: 250)
containerViewHeight.isActive = true
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
])
}
#objc private func containerViewTapped() {
isViewTapped.toggle()
containerViewHeight.constant = isViewTapped ? 30 : 250
innerMinView.isHidden = !isViewTapped
innerMaxView.isHidden = isViewTapped
}
private func configureCollectionView() {
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: UICollectionViewFlowLayout())
view.addSubview(collectionView)
collectionView.backgroundColor = .systemYellow
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: containerView.bottomAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
private func configureInnerMinView() {
innerMinView = UIView()
innerMinView.backgroundColor = .systemRed
innerMinView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(innerMinView)
NSLayoutConstraint.activate([
innerMinView.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: 0.8),
innerMinView.heightAnchor.constraint(equalTo: containerView.heightAnchor, multiplier: 0.8),
innerMinView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
innerMinView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
])
}
private func configureInnerMaxView() {
innerMaxView = UIView()
innerMaxView.backgroundColor = .systemBlue
innerMaxView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(innerMaxView)
NSLayoutConstraint.activate([
innerMaxView.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: 0.8),
innerMaxView.heightAnchor.constraint(equalTo: containerView.heightAnchor, multiplier: 0.8),
innerMaxView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
innerMaxView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor)
])
}
side note: btw. I think I know what project this is. :) SA-GH.F
You're activating conflicting constraints on top of each other. If you want to go between a minimized and maximized view, just change the constant value on the height constraint itself.
I whipped up a sample MyViewController in playgrounds with 2 views, the first of which's height and color changes when you tap it. myView is like your collapsable view, and myOtherView is like your collectionView.
Let me know if you have any questions!
class MyViewController : UIViewController {
private enum ViewState {
case min
case max
var height: CGFloat {
switch self {
case .min:
return 30
case .max:
return 250
}
}
var color: UIColor {
switch self {
case .min:
return .red
case .max:
return .green
}
}
}
private var myViewState: ViewState = .min {
didSet {
viewStateToggled()
}
}
private let myView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let myOtherView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .yellow
return view
}()
private lazy var myViewHeightConstraint = NSLayoutConstraint(
item: myView,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 1,
constant: myViewState.height
)
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
}
override func viewDidLoad() {
super.viewDidLoad()
configureMyViews()
}
private func configureMyViews() {
view.addSubview(myView)
myView.backgroundColor = myViewState.color
NSLayoutConstraint.activate([
myView.topAnchor.constraint(equalTo: view.topAnchor),
myView.leftAnchor.constraint(equalTo: view.leftAnchor),
myView.rightAnchor.constraint(equalTo: view.rightAnchor),
myViewHeightConstraint
])
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
myView.addGestureRecognizer(tap)
view.addSubview(myOtherView)
NSLayoutConstraint.activate([
myOtherView.topAnchor.constraint(equalTo: myView.bottomAnchor),
myOtherView.leftAnchor.constraint(equalTo: view.leftAnchor),
myOtherView.rightAnchor.constraint(equalTo: view.rightAnchor),
myOtherView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
private func viewStateToggled() {
myViewHeightConstraint.constant = myViewState.height
UIView.animate(withDuration: 0.4) {
self.myView.backgroundColor = self.myViewState.color
self.view.layoutSubviews()
}
}
#objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
myViewState = myViewState == .min ? .max : .min
}
}
I was wondering if you need a call to update your layout. Maybe called from the resizeCollectionView()?
let bottomAnchorMinContraint: NSConstraint = collectionView.topAnchor.constraint(equalTo: minView.bottomAnchor)
let bottomAnchorMaxContraint: NSConstraint = collectionView.topAnchor.constraint(equalTo: maxView.bottomAnchor)
if isMaxViewHidden {
bottomAnchorMinContraint.isActive = true
bottomAnchorMaxContraint.isActive = false
}
if !isMaxViewHidden {
bottomAnchorMinContraint.isActive = false
bottomAnchorMaxContraint.isActive = true
}
view.layoutIfNeeded()

Updating layout anchors not working as expected

I have created a view with height 100 using NSLayout anchors. When I'm trying to update that on button click, it's not working.
I have tried below code, but it's not working.
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
let viewAnimate = UIView()
var isHidden = false
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(viewAnimate)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewAnimate.translatesAutoresizingMaskIntoConstraints = false
viewAnimate.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 8).isActive = true
viewAnimate.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -8).isActive = true
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
viewAnimate.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewAnimate.backgroundColor = UIColor.red
}
#IBAction func show() {
if !isHidden {
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 200).isActive = true
button.setTitle("Show", for: .normal)
} else {
button.setTitle("Hide", for: .normal)
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
}
UIView.animate(withDuration: 1) {
self.viewAnimate.layoutIfNeeded()
}
isHidden = !isHidden
}
}
View should change the height based on height constraint
Your current code creates conflicts as every line like viewAnimate.topAnchor.constraint(equalTo: adds a new constraint , create a var
var topCon:NSLayoutConstraint!
topCon = viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100)
topCon.isActive = true
#IBAction func show() {
if !isHidden {
topCon.constant = 200
button.setTitle("Show", for: .normal)
} else {
button.setTitle("Hide", for: .normal)
topCon.constant = 100
}
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
isHidden = !isHidden
}

Animate constraints change UIViewController

I have "error" view, that is placed above navigation bar and hidden. When error occured, i want that view to smoothly show from top. I tried:
class AuthViewController: UIViewController {
let error: ErrorView = {
let error = ErrorView()
error.setup()
return error
}()
var topAnchor: NSLayoutConstraint!
var botAnchor: NSLayoutConstraint!
override func viewDidLoad() {
setupErrorView()
}
private func setupErrorView(){
view.addSubview(error)
botAnchor = error.bottomAnchor.constraint(equalTo: view.topAnchor)
botAnchor.isActive = true
topAnchor = error.topAnchor.constraint(equalTo: view.topAnchor, constant: CGFloat(Offsets.navigationAndStatusBarHeight))
topAnchor.isActive = false
error.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
error.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
func showError(_ text: String){
UIView.animate(withDuration: 2.0) {[weak self] in
guard let weakSelf = self else { return }
print("attempt to animate")
weakSelf.error.show(text)
weakSelf.botAnchor.isActive = false
weakSelf.topAnchor.isActive = true
weakSelf.view.setNeedsLayout()
}
}
}
class ErrorView: UIView {
private var label: UILabel = {
return LabelSL.regular()
}()
fileprivate func setup(){
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = Theme.Color.orange.value
addSubview(label)
}
fileprivate func show(_ text: String){
let sideOffset: CGFloat = 10
let verticalOffset: CGFloat = 10
label.text = text
label.topAnchor.constraint(equalTo: topAnchor, constant: verticalOffset).isActive = true
label.leftAnchor.constraint(equalTo: leftAnchor, constant: sideOffset).isActive = true
label.rightAnchor.constraint(equalTo: rightAnchor, constant: -sideOffset).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -verticalOffset).isActive = true
}
}
Animation should be done when func showError(_ text: String){ method called, but it's not. View just appear instantly.
You're trying to animate constraints in wrong way. You should set constraints outside of animation block and only layoutIfNeeded in animation:
func showError(_ text: String){
botAnchor.isActive = false
topAnchor.isActive = true
error.show(text)
UIView.animate(withDuration: 2.0) {
self.view.layoutIfNeeded()
}
}

Swift textview growing direction

so i have a textview inside a uiview. And my question is how to make my textivew grow in up direction when textview goes to the next line as well as my uiview.
var textheightcontraint : NSLayoutConstraint!
var viewheightconstraint : NSLayoutConstraint!
func setup4(){
view.addSubview(colorview)
colorview.topAnchor.constraint(equalTo: customtableview.bottomAnchor).isActive = true
colorview.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
colorview.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
viewheightconstraint = colorview.heightAnchor.constraint(equalToConstant: 44)
viewheightconstraint.isActive = true
colorview.backgroundColor = UIColor.lightGray
colorview.addSubview(customtextview2)
customtextview2.backgroundColor = .white
customtextview2.leftAnchor.constraint(equalTo: colorview.leftAnchor).isActive = true
customtextview2.rightAnchor.constraint(equalTo: colorview.rightAnchor, constant: -20).isActive = true
customtextview2.bottomAnchor.constraint(equalTo: colorview.bottomAnchor).isActive = true
textheightcontraint = customtextview2.heightAnchor.constraint(equalToConstant: 39)
textheightcontraint.isActive = true
customtextview2.delegate = self
}
func setuptextview(){
let fixedWidth = customtextview2.frame.size.width
let newSize = customtextview2.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
self.textheightcontraint.constant = newSize.height
self.viewheightconstraint.constant = newSize.height
self.view.layoutIfNeeded()
}
func textViewDidChange(_ textView: UITextView) {
setuptextview()
}
If I understand your question, you want the text view (and the view it's contained in) to keep its bottom at the same position, and expand upward as you type?
To do that, disable scrolling in the text view, and set up your constraints so the bottom of the containing view is constrained to a y-position:
//
// ViewController.swift
//
// Created by Don Mag on 8/9/18.
//
import UIKit
class ViewController: UIViewController {
var theContainingView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var theTextView: UITextView = {
let v = UITextView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .yellow
theContainingView.backgroundColor = .cyan
theContainingView.addSubview(theTextView)
view.addSubview(theContainingView)
NSLayoutConstraint.activate([
theTextView.topAnchor.constraint(equalTo: theContainingView.topAnchor, constant: 8.0),
theTextView.bottomAnchor.constraint(equalTo: theContainingView.bottomAnchor, constant: -8.0),
theTextView.leadingAnchor.constraint(equalTo: theContainingView.leadingAnchor, constant: 8.0),
theTextView.trailingAnchor.constraint(equalTo: theContainingView.trailingAnchor, constant: -8.0),
theContainingView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40.0),
theContainingView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40.0),
// Constrain the Bottom of the containing view. As the textView grows (or shrinks) with input,
// the TOP of the view will move up or down
theContainingView.bottomAnchor.constraint(equalTo: view.topAnchor, constant: 300.0),
])
theTextView.isScrollEnabled = false
theTextView.text = "This is the starting text."
}
}
Results:

How to set constraint relationship to view, not subviews programmatically?

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:

Resources