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

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:

Related

TableView not appearing after setting the constraints programmatically

I have a UITableView in my storyboard. I am trying to setup constraints for it in my view controller. After running the application it's not showing up at all. it's only working when i don't run the constraints. here are my constraints.
func tableviewsConstraints(){
homeTableView.translatesAutoresizingMaskIntoConstraints = false
homeTableView.layer.cornerRadius = 10
homeTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
homeTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
homeTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50).isActive = true
homeTableView.heightAnchor.constraint(equalToConstant: homeTableView.rowHeight * 3).isActive = true
self.view.addSubview(homeTableView)
}
override func viewDidLoad() {
super.viewDidLoad()
tableviewsConstraints()
Add the homeTableView as the view's subview before adding the constraints to it. And use a constant value when setting the height of tableView, i.e.
func tableviewsConstraints(){
self.view.addSubview(homeTableView) //here....
homeTableView.layer.cornerRadius = 10
homeTableView.translatesAutoresizingMaskIntoConstraints = false
homeTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
homeTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
homeTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50).isActive = true
let rowHeight: CGFloat = 100.0 //here....
homeTableView.heightAnchor.constraint(equalToConstant: rowHeight * 3).isActive = true
}
Change the rowHeight value as per your requirement.
Don't you have the outlet of the table view if you have it in the storyboard? Then why you need to subview? If you're creating table view programmatically then follow this.

UIScrollview does not show all subviews and is not scrollable

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

Gif not appearing on manually entered constraints

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
}

Positioning constraints not having any effect

I am trying to code the label in the viewDidLoad function. The label is showing up but the code I am using now is not affecting the positioning. I am trying to code everything in the viewDidLoad function. You can see the screenshot below.
override func viewDidLoad() {
super.viewDidLoad()
let backbutton = UILabel()
backbutton.backgroundColor = UIColor.orange
backbutton.translatesAutoresizingMaskIntoConstraints = false
backbutton.widthAnchor.constraint(equalToConstant: 300).isActive = true
backbutton.heightAnchor.constraint(equalToConstant: 300).isActive = true
backbutton.centerXAnchor.constraint(equalTo: backbutton.centerXAnchor, constant: 100).isActive = true
backbutton.centerYAnchor.constraint(equalTo: backbutton.centerYAnchor, constant: 300).isActive = true
view.addSubview(backbutton)
}
This is how you can align the label into the center of the view,
let backbutton = UILabel()
view.addSubview(backbutton)
backbutton.backgroundColor = UIColor.orange
backbutton.translatesAutoresizingMaskIntoConstraints = false
backbutton.widthAnchor.constraint(equalToConstant: 300).isActive = true
backbutton.heightAnchor.constraint(equalToConstant: 300).isActive = true
backbutton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
backbutton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
When you are setting constraints programmatically, make sure you add the view into the super/parent view before applying constraints. Secondly in the below lines, you are telling the backButton label to align its center to itself (i.e, backbutton.centerXAnchor.constraint(equalTo: backbutton.centerXAnchor).
backbutton.centerXAnchor.constraint(equalTo: backbutton.centerXAnchor, constant: 100).isActive = true
backbutton.centerYAnchor.constraint(equalTo: backbutton.centerYAnchor, constant: 300).isActive = true
As you want to align it center vertically and horizontally to its parent view so you should set the center constraints equal to parent view as below,
backbutton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
backbutton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
Try this!!
self.view.setNeedsUpdateConstraints()
self.view.layoutIfNeeded()
Try using CGRect instead:
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
let xPostion:CGFloat = screenWidth - 150 //150 is half of view's width
let yPostion:CGFloat = screenHeight - 150 //150 is half of view's height
let buttonWidth:CGFloat = 300
let buttonHeight:CGFloat = 300
backbutton.frame = CGRect(x:xPostion, y:yPostion, width:buttonWidth, height:buttonHeight)
instead of screen width/height you can also try to get the width of your super view using:
self.view.frame.width
self.view.frame.height

Programmatically layed out UIScrollView, and added auto layout to it's subviews, but it does not scroll

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

Resources