Swift Programmatic Constraint not working - ios

I am trying to create a uiview that has a segment control inside. I want to be able to add this uiview to my viewcontroller's view. the segment control should be right on top of my tableview. but everytime i setup the constraints i keep getting this error
"Thread 1: Exception: "Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x282ee24c0 "UIView:0x119d3a610.bottom"> and <NSLayoutYAxisAnchor:0x282ee2500 "UITableView:0x11a014a00.top"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal."" I tried working around by adding the subview first and what not but it's not working. here's my code if anyone can help me.
func configureTableView(){
setupSegmentControl()
view.addSubview(tableView)
setTableViewDelegates()
tableView.rowHeight = 50
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.tableView.topAnchor.constraint(equalToSystemSpacingBelow: self.view.topAnchor, multiplier: 20).isActive = true
tableView.register(UINib(nibName: "CustomCellNSB2", bundle: nil), forCellReuseIdentifier: "CustomCellNSB2")
}
func setTableViewDelegates(){
tableView.delegate = self
tableView.dataSource = self
}
func setupSegmentControl(){
var headerView = UIView()
var importanceSegmentControl = CustomSegmentControl()
headerView.addSubview(importanceSegmentControl)
self.view.addSubview(headerView)
importanceSegmentControl.addTarget(self, action: #selector(indexChanged(control:)),for: UIControl.Event.valueChanged)
headerView.translatesAutoresizingMaskIntoConstraints = false
headerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
headerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
headerView.bottomAnchor.constraint(equalTo: self.tableView.topAnchor, constant: 20).isActive = true
headerView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 10).isActive = true
importanceSegmentControl.translatesAutoresizingMaskIntoConstraints = false
importanceSegmentControl.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 20).isActive = true
importanceSegmentControl.trailingAnchor.constraint(equalTo: headerView.trailingAnchor, constant: -20).isActive = true
importanceSegmentControl.bottomAnchor.constraint(equalTo: headerView.topAnchor, constant: 20).isActive = true
importanceSegmentControl.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 10).isActive = true
}

The tableView and importanceSegmentControl doesn't have any common ancestor at the time of adding the constraint to the importanceSegmentControl. So to fix the issue just switch the order of execution:
func configureTableView(){
view.addSubview(tableView)
setupSegmentControl()
//...
}

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.

Swift - auto constrain items in ScrollView

I am having problems to constrain my items inside my UIScrollView, to be more specific trailing - anchors are behaving weird:
As you can see trailing-anchors are not the same as leading-anchors..
These are my constrains:
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 130).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
emailTextField.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
emailTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
emailTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
emailTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
anzeigeNameTextField.topAnchor.constraint(equalTo: emailTextField.topAnchor, constant: 80).isActive = true
anzeigeNameTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
anzeigeNameTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
anzeigeNameTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
wishlistHandleTextField.topAnchor.constraint(equalTo: anzeigeNameTextField.topAnchor, constant: 80).isActive = true
wishlistHandleTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
wishlistHandleTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
wishlistHandleTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
passwordTextField.topAnchor.constraint(equalTo: wishlistHandleTextField.topAnchor, constant: 80).isActive = true
passwordTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
passwordTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
passwordTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
eyeButtonOne.centerYAnchor.constraint(equalTo: passwordTextField.centerYAnchor, constant: 10).isActive = true
eyeButtonOne.trailingAnchor.constraint(equalTo: passwordTextField.trailingAnchor).isActive = true
passwordWiederholenTextField.topAnchor.constraint(equalTo: passwordTextField.topAnchor, constant: 80).isActive = true
passwordWiederholenTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
passwordWiederholenTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
passwordWiederholenTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
eyeButtonTwo.centerYAnchor.constraint(equalTo: passwordWiederholenTextField.centerYAnchor, constant: 10).isActive = true
eyeButtonTwo.trailingAnchor.constraint(equalTo: passwordWiederholenTextField.trailingAnchor).isActive = true
documentsLabel.topAnchor.constraint(equalTo: passwordWiederholenTextField.topAnchor, constant: 80).isActive = true
documentsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
documentsLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
documentsLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
signUpButton.topAnchor.constraint(equalTo: documentsLabel.topAnchor, constant: 80).isActive = true
signUpButton.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
signUpButton.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
signUpButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
What am I doing wrong? Do I have to constraint differently inside a UIScrollView? And if so, how and why?
create sample code for you. Hope it will be useful
and read this link for better sene below code: scrollView with auto layout
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = UIColor.red.withAlphaComponent(0.5)
// create scrollView
let scrollView = UIScrollView.init()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = UIColor.blue.withAlphaComponent(0.5)
self.view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
// create tempView inside scrollView for use autolayout with scrollView
let tempView = UIView.init()
tempView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(tempView)
tempView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
tempView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
tempView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
tempView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
// is important, just for use autolayout inside scrollView with scroll if content large screen
tempView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
let heightConstraint = tempView.heightAnchor.constraint(equalTo: self.view.heightAnchor)
heightConstraint.priority = .init(250)
heightConstraint.isActive = true
// create sample UI inside tempView
let emailTextField = UITextField.init()
emailTextField.translatesAutoresizingMaskIntoConstraints = false
emailTextField.backgroundColor = .white
tempView.addSubview(emailTextField)
emailTextField.topAnchor.constraint(equalTo: tempView.topAnchor, constant: 200).isActive = true
emailTextField.leadingAnchor.constraint(equalTo: tempView.leadingAnchor, constant: 50).isActive = true
emailTextField.trailingAnchor.constraint(equalTo: tempView.trailingAnchor, constant: -50).isActive = true
emailTextField.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
screenShot
uiview needed to be subview of scrollview, after that you can embed you're elements into uiview

What is the difference of implementation between UITableView & UICollectionView when using Xib?

I try to implement TableView and CollectionView with Xib Files. I implement UITableview properly but when I try to implement ColletionView I get an error "libc++abi.dylib: terminating with uncaught exception of type NSException". I got this error in var collectionView = UICollectionView() line.
Why I dont get this error this line var tableView = UITableView() ? And where is my mistake ? And What is the solution ? Thanks in advance.
var tableView = UITableView()
var collectionView = UICollectionView()
override func viewDidLoad() {
super.viewDidLoad()
let headerMenu = UIView()
headerMenu.translatesAutoresizingMaskIntoConstraints = false
headerMenu.backgroundColor = .green
self.view.addSubview(headerMenu)
headerMenu.heightAnchor.constraint(equalToConstant: 48.0).isActive = true
headerMenu.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
headerMenu.topAnchor.constraint(equalTo: view.safeTopAnchor).isActive = true
collectionView.delegate = self
collectionView.dataSource = self
collectionView.translatesAutoresizingMaskIntoConstraints = false
headerMenu.addSubview(collectionView)
collectionView.topAnchor.constraint(equalTo: headerMenu.topAnchor, constant: 0).isActive = true
collectionView.bottomAnchor.constraint(equalTo: headerMenu.bottomAnchor, constant: -10).isActive = true
collectionView.rightAnchor.constraint(equalTo: headerMenu.rightAnchor, constant: -10).isActive = true
collectionView.leftAnchor.constraint(equalTo: headerMenu.leftAnchor, constant: 10).isActive = true
collectionView.register(UINib(nibName: "MenuCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "MenuCollectionViewCell")
let mainView = UIView()
mainView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(mainView)
mainView.topAnchor.constraint(equalTo: headerMenu.bottomAnchor, constant: 0).isActive = true
mainView.bottomAnchor.constraint(equalTo: view.safeBottomAnchor, constant: 0).isActive = true
mainView.rightAnchor.constraint(equalTo: view.safeRightAnchor, constant: 0).isActive = true
mainView.leftAnchor.constraint(equalTo: view.safeLeftAnchor, constant: 0).isActive = true
mainView.backgroundColor = UIColor.white
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.estimatedRowHeight = UITableViewAutomaticDimension
mainView.addSubview(tableView)
tableView.topAnchor.constraint(equalTo: mainView.topAnchor, constant: 0).isActive = true
tableView.bottomAnchor.constraint(equalTo: mainView.bottomAnchor, constant: -10).isActive = true
tableView.rightAnchor.constraint(equalTo: mainView.rightAnchor, constant: -10).isActive = true
tableView.leftAnchor.constraint(equalTo: mainView.leftAnchor, constant: 10).isActive = true
tableView.separatorStyle = .none
tableView.register(UINib(nibName: "ProductTableViewCell", bundle: nil), forCellReuseIdentifier: "ProductTableViewCell")
}
You need to use other init to create UICollectionView instance:
let layout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: rect, collectionViewLayout: layout)
UICollectionView requires a UICollectionViewLayout in order to render. When creating a collection view via the storyboard this is already set with a default value.
You can initialise it like this:
let flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: flowLayout)
Flow layout
A concrete layout object that organizes items into a grid with optional header and footer views for each section.
The flow layout is one of the default/built in options and is useful for creating a 'bookshelf' style grid in which the columns fill left to right and then top to bottom (unless you change the width).
There is a good tutorial on the Ray Wenderlich website which explains more about collection views and their layouts

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:

Remove constraints iOS

I try to remove constraints. I want to different constraints on portrait and landscape. If I change the orientation to Portrait I call the function setupConstrainsInPortrait and conversely. I have two functions.
This function setup Portrait mode.
func setupConstrainsInPortrait() {
view.addSubview(myView)
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
myView.heightAnchor.constraint(equalToConstant: 300).isActive = true
myView.widthAnchor.constraint(equalToConstant: view.frame.size.width).isActive = true
view.addSubview(switchKmM)
switchKmM.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
switchKmM.topAnchor.constraint(equalTo: myView.bottomAnchor, constant: 10).isActive = true
switchKmM.heightAnchor.constraint(equalToConstant: 50).isActive = true
switchKmM.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
view.addSubview(speedLbl)
speedLbl.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
speedLbl.topAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
speedLbl.heightAnchor.constraint(equalToConstant: 50).isActive = true
speedLbl.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
}
This function setup landscape mode.
func setupConstrainsInLandScape() {
view.addSubview(myView)
myView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
myView.heightAnchor.constraint(equalToConstant: 300).isActive = true
myView.widthAnchor.constraint(equalToConstant: 150).isActive = true
view.addSubview(switchKmM)
switchKmM.leftAnchor.constraint(equalTo: myView.rightAnchor, constant: 30).isActive = true
switchKmM.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
switchKmM.heightAnchor.constraint(equalToConstant: 50).isActive = true
switchKmM.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
}
I use these functions in viewWillTransition
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
print("landscape!")
view.backgroundColor = .green
setupConstrainsInLandScape()
self.viewWillLayoutSubviews()
}
else {
print("portrét")
view.backgroundColor = .white
setupConstrainsInPortrait()
self.viewWillLayoutSubviews()
}
super.viewWillTransition(to: size, with: coordinator)
}
The problem is, that constraints(from portrait function) in landscape mode aren't deleted
I hope that someone can help me... Thank you
You're not doing enough work. You need to retain references to all the constraints you activate, so that you can deactivate them later. For example, you are saying:
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
myView.heightAnchor.constraint(equalToConstant: 300).isActive = true
myView.widthAnchor.constraint(equalToConstant: view.frame.size.width).isActive = true
Instead, you need to say something like
self.myConstraints1 = [
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
myView.heightAnchor.constraint(equalToConstant: 300),
myView.widthAnchor.constraint(equalToConstant: view.frame.size.width),
]
Now activate all those constraints. Proceed the same way throughout.
Thus, when the time comes to deactivate constraints because the orientation is changing, you have references to them and can do so.
You can remove your constraints using:
myView.constraints.removeAll()
Before setting the new ones.

Resources