I'm trying to center my subview with a button in its superview. So I want the center of the subview be the center of the superview. I'm trying that with following code:
override func viewDidLoad() {
self.view.backgroundColor = UIColor.redColor()
var menuView = UIView()
var newPlayButton = UIButton()
//var newPlayImage = UIImage(named: "new_game_button_5cs")
var newPlayImageView = UIImageView(image: UIImage(named: "new_game_button_5cs"))
newPlayButton.frame = CGRectMake(0, 0, newPlayImageView.frame.width, newPlayImageView.frame.height)
newPlayButton.setImage(newPlayImage, forState: .Normal)
newPlayButton.backgroundColor = UIColor.whiteColor()
menuView.backgroundColor = UIColor.whiteColor()
menuView.addSubview(newPlayButton)
menuView.addConstraint(
NSLayoutConstraint(item: self.view,
attribute: .CenterX,
relatedBy: .Equal,
toItem: menuView,
attribute: .CenterX,
multiplier: 1, constant: 0)
)
}
Unfortunately the program breaks when I try to run it.
(Thread 1: signal SIGABRT)
Your code triggers an assertion saying:
When added to a view, the constraint's items must be descendants of
that view (or the view itself).
This means you have to add menuView as a subview to self.view before adding constraints. You should also add the constraints to self.view, not the menuView. Last but not least, remove autoresizing masks constraints that were implicitly added to menuView by calling setTranslatesAutoresizingMaskIntoConstraints(false) or autolayout will complain about conflicting constraints.
menuView.addSubview(newPlayButton)
menuView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(menuView)
self.view.addConstraint(
NSLayoutConstraint(item: self.view,
attribute: .CenterX,
relatedBy: .Equal,
toItem: menuView,
attribute: .CenterX,
multiplier: 1, constant: 0)
)
Related
Currently, I have a UICollectionViewCell used in horizontal scroll UICollectionView. I have tried the following to centre the icon to the centre.
class BasicCell: UICollectionViewCell {
var tabInfo: TabInfo?
let imageView: UIImageView = {
let image = UIImage(systemName: "gear")
let imageView = UIImageView(image: image)
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(imageView)
// Hoping we can center imageView. But it would not work? Why?
addConstraint(NSLayoutConstraint.init(item: imageView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint.init(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
BasicCell.topRoundCorners(self.contentView)
}
Here's the outcome
I expect the following code can centre the UIImageView without issue. However, it doesn't help
addConstraint(NSLayoutConstraint.init(item: imageView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint.init(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
At first, I was thinking to give up the programatically way, and create UI layout via storyboard. However, in my storyboard, there is no UICollectionViewController. (The top tab bar component, is a UIView holding a UICollectionView created programatically).
Without UICollectionViewController, I don't find a way to drag and drop a UICollectionViewCell into the storyboard.
Do you have any idea, how I can centre my UIImageView, with correct programatically code? Or, should I try to fix this problem with XIB? Thanks.
by default, the UIView create programaticly has translatesAutoresizingMaskIntoConstraints = true, change it to false to enable auto layout :
imageView.translatesAutoresizingMaskIntoConstraints = false
let iamgeViewConstraints = [
imageView.centerXAnchor.constraint(equalTo: centerXAnchor),
imageView.centerYAnchor.constraint(equalTo: centerYAnchor)
]
NSLayoutConstraint.activate(iamgeViewConstraints)
Try to set imageView.translatesAutoresizingMaskIntoConstraints = false that tells iOS not to create auto layout constraints programmatically. Also, you can set constraints using frame like
imageView.frame = CGRect(x: superView.frame.width / 2, y: superView.frame.height / 2, width: imageViewWidth, height: imageViewHeight)
then you don't require imageView.translatesAutoresizingMaskIntoConstraints = false
I want to add container view to the main view (rootViewController.view), but the following gives exception. I know it about the constraints but not able to find out why.
import UIKit
class rootViewController : UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
setupLoginView()
}
func setupLoginView() {
// User ID label
let userIDLabel:UILabel = UILabel()
userIDLabel.text = "User ID"
// Password label
let passwordLabel:UILabel = UILabel()
passwordLabel.text = "Password"
// User ID text
let userIDText:UITextField = UITextField()
// Password text
let passwordText:UITextField = UITextField()
// Login button
let loginBtn:UIButton = UIButton()
loginBtn.setTitle("Login", for: .normal)
// Container view
let container:UIView = UIView()
container.addSubview(userIDLabel)
container.addSubview(userIDText)
container.addSubview(passwordLabel)
container.addSubview(passwordText)
container.addSubview(loginBtn)
view.addSubview(container)
// Add constraints
let heightConstraint = NSLayoutConstraint(item: container, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 300)
let widthConstraint = NSLayoutConstraint(item: container, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200)
let centerXConstraint = NSLayoutConstraint(item: container, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0)
let centerYConstraint = NSLayoutConstraint(item: container, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)
container.addConstraint(heightConstraint)
container.addConstraint(widthConstraint)
container.addConstraint(centerXConstraint)
container.addConstraint(centerYConstraint)
}
}
Gives the following exception, with hint "Does the constraint reference something from outside the subtree of the view? That's illegal"
'NSGenericException', reason: 'Unable to install constraint on view.
Does the constraint reference something from outside the subtree of
the view? That's illegal. constraint: NSLayoutConstraint:0x170089830
UIView:0x12de138c0.centerX == UIView:0x12de0e650.centerX (active)>
Replace your code for adding the center constraints with these lines:
view.addConstraint(centerXConstraint)
view.addConstraint(centerYConstraint)
You can't add a constaint on the container with a reference to it's superview.
And move the setupLoginView() to viewDidLoad instead of init().
Don't forget to remove the warning in the console to set translatesAutoresizingMaskIntoConstraints to false for all the created views (container, labels, buttons and textviews).
1. Move setupLoginView() to viewDidLoad()
override func viewDidLoad()
{
super.viewDidLoad()
setupLoginView()
}
2. Set translatesAutoresizingMaskIntoConstraints of container
container.translatesAutoresizingMaskIntoConstraints = false
3. Add constraints to relevant views:
container.addConstraint(heightConstraint)
container.addConstraint(widthConstraint)
view.addConstraint(centerXConstraint)
view.addConstraint(centerYConstraint)
The ViewController's view does not exist yet. Move the setupLoginView() call from the init() method to viewDidLoad().
Also - as #Pranav Kasetti suggests - you have to add the center constraints to the view instead of the container.
Last but not least set translatesAutoresizingMaskIntoConstraints to false for all the created views (including the container).
The problem is that you define properties in the init method, but it's not the good entry point for a ViewController. You should take a look here for some details.
Then, replace
init() {
super.init(nibName: nil, bundle: nil)
by this:
override func viewDidLoad() {
super.viewDidLoad()
and it should works.
I'm allowing the user to create a new text field when they press a button. I want to programmatically copy the leading and trailing constraints from an existing text field. My code:
#IBAction func addAnotherTextField(sender: AnyObject) {
let newTextField = UITextField.init(frame: CGRectMake(20, positionY, self.view.frame.size.width-40, 30))
newTextField.delegate = self
newTextField.tag = fieldCount
newTextField.placeholder = "You created this!"
newTextField.borderStyle = UITextBorderStyle.RoundedRect
newTextField.translatesAutoresizingMaskIntoConstraints = false
let leadingConstraint = NSLayoutConstraint(item: newTextField, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: nameTextField, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: newTextField, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: nameTextField, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
newTextField.addConstraint(leadingConstraint)
newTextField.addConstraint(trailingConstraint)
view.addSubview(newTextField)
fieldCount++
positionY = positionY + 15 + newTextField.frame.size.height
}
}
Unfortunately, the above code crashes at run time.
You need first to addSubView, only then to add the constraints.
You can't connect constraints between UIViews that are not related...
From the docs:
Discussion:
The constraint must involve only views that are within scope of the receiving view. Specifically, any views involved must be either the receiving view itself, or a subview of the receiving view. Constraints that are added to a view are said to be held by that view. The coordinate system used when evaluating the constraint is the coordinate system of the view that holds the constraint.
I have to dynamically create some UILabel and UITextViews according to some Data ~ around 20 - all of them with dynamic height/lines of Text for an IOS App in Swift.
Since the screen is not large enough I am adding the Views to a ScrollView, but unfortunately the contentsize property of my ScrollView seems not to receive the proper values.
I'm debugging since a couple of hours and tried different set ups but so far none of them worked out.
The sizing is done in a custom method refreshUI() which gets firstly called in viewDidLoad(). The ViewController simply contains one centred Heading Label and the ScrollView which fills the rest of the space (pinned to Top, Left, Right, Bottom with 8.0).
Then I'm trying to populate my Data in that scrollView as follows:
func refreshUI(){
println("refreshUI()")
questionnaireTitle.text = site.questionnaireName
let questionnaire = site.questionnaire
//removes all SubViews in ScrollView
clearView(scrollView)
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
for questionGroup in questionnaire{
//QUESTIONGROUP HEADING
let questionGroupHeading = UILabel()
questionGroupHeading.setTranslatesAutoresizingMaskIntoConstraints(false)
questionGroupHeading.text = questionGroup.questionsHeading
questionGroupHeading.sizeToFit()
scrollView.addSubview(questionGroupHeading)
viewStack.append(questionGroupHeading)
//QUESTION
for question in questionGroup.questions{
//QUESTION LABEL
let questionLabel = UILabel()
questionLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
questionLabel.text = question.text
questionLabel.numberOfLines = 0
questionLabel.lineBreakMode = .ByWordWrapping
questionLabel.sizeToFit()
scrollView.addSubview(questionLabel)
viewStack.append(questionLabel)
if question.type == "selector"{
//SELECTOR QUESTION
println("selector Question")
for statement in question.statements{
//TODO add Statement + Picker
}
}
else if question.type == "standard"{
//STANDARD QUESTION
println("standard question")
let answerLabel = UITextField()
answerLabel.placeholder = "here goes your answer"
answerLabel.sizeToFit()
answerLabel.backgroundColor = UIColor.whiteColor()
answerLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
scrollView.addSubview(answerLabel)
viewStack.append(answerLabel)
}
}
}
//setUpConstraints
var counter = 0
var height:CGFloat = 0.0
for view in viewStack{
let rightConst = NSLayoutConstraint(item: view, attribute: .Right, relatedBy: .Equal, toItem: scrollView, attribute: .Right, multiplier: 1.0, constant: 0.0)
let leftConst = NSLayoutConstraint(item: view, attribute: .Left, relatedBy: .Equal, toItem: scrollView, attribute: .Left, multiplier: 1.0, constant: 0.0)
let widthConst = NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: scrollView.frame.width)
view.addConstraint(widthConst)
scrollView.addConstraint(leftConst)
scrollView.addConstraint(rightConst)
//pin first view to top of scrollView
if counter == 0{
let topConst = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: scrollView, attribute: .Top, multiplier: 1.0, constant: 0.0)
scrollView.addConstraint(topConst)
}
//pin all other views to the top of the previousView
else{
let topConst = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: viewStack[counter - 1], attribute: .Bottom, multiplier: 1.0, constant: 8.0)
scrollView.addConstraint(topConst)
}
counter++
height += view.bounds.height
}
let contentSize = CGSize(width: scrollView.frame.width, height: height)
scrollView.contentSize = contentSize
scrollView.contentOffset = CGPoint(x: 0, y: 0)
println("refreshUI() done")
}
The ScrollView is not vertically scrollable, although some content is not displayed due to being out of the screen.
But it scrolls horizontally, although I'm setting the width of each view to the width of the SCrollView, which should mean that every SubView is just as big as the size of the ScrollView and thus not vertically scrollable.
If you are using AutoLayout then this tutorial will be useful for implementing scrollview.
When you run refreshUI() inside of viewDidLoad(), the subviews that are being created are using the Interface Builder defaults for the parent views instead of the actual sizes on the device. This is likely why your sizes are not what you expect. If you run refreshUI() inside of viewDidLayoutSubviews() instead, then the subviews will correctly read the widths and heights of the parent view.
I am trying to center my subview with a button in its superview. (I want tha distance from top-to-screen and bottom-to-screen be the same and the distance from right-to-screen and left-to-screen be the same) So I want the center of the subview be the center of the superview. I am trying that with following code:
override func viewDidLoad() {
self.view.backgroundColor = UIColor.redColor()
var menuView = UIView()
var newPlayButton = UIButton()
var newPlayImage = UIImage(named: "new_game_button_5cs")
var newPlayImageView = UIImageView(image: UIImage(named: "new_game_button_5cs"))
newPlayButton.frame = CGRectMake(0, 0, newPlayImageView.frame.width, newPlayImageView.frame.height)
//newPlayButton.setImage(newPlayImage, forState: .Normal)
menuView.backgroundColor = UIColor.whiteColor()
menuView.addSubview(newPlayButton)
self.view.addSubview(menuView)
menuView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addConstraint(
NSLayoutConstraint(item: self.view,
attribute: .CenterX,
relatedBy: .Equal,
toItem: menuView,
attribute: .CenterX,
multiplier: 1, constant: 0)
)
self.view.addConstraint(
NSLayoutConstraint(item: self.view,
attribute: .CenterY,
relatedBy: .Equal,
toItem: menuView,
attribute: .CenterY,
multiplier: 1, constant: 0)
)
}
But when I run the code I get the following: