Getting run time error when adding constraints - swift

I'm trying to center an image to a view but I'm getting error..this is my code:
let markerimage = #imageLiteral(resourceName: "ic_new_mark_icon")
let size = CGSize(width: 60, height: 60)
var newimage = imageWithImage(image: markerimage, scaledToSize: size)
var imageview = UIImageView(image: newimage)
self.view.addSubview(imageview)
let xConstraint = NSLayoutConstraint(item: imageview, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: imageview, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: 0)
imageview.addConstraint(xConstraint)
imageview.addConstraint(yConstraint)
and my error is
2016-12-22 16:29:11.305417 Googlemappractice[21426:362773] [LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x600000285f00 UIImageView:0x7ff772f15560.centerX == UIView:0x7ff772c0bd30.centerX (inactive)>
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout)

The constraints need to be added to the earliest common ancestor of the two views affected by the constraint. In this case, self.view is earlier in the hierarchy so the constraints should be added to it instead of to imageview.
Since iOS 8, there is an easier way. Once you've added your view to the view hierarchy (with addSubview), you can activate the constraints instead of adding them to views.
You can do this by setting the isActive property to true:
xConstraint.isActive = true
yConstraint.isActive = true
or by using the activate class function of NSLayoutConstraint to active multiple constraints:
NSLayoutConstraint.activate([xConstraint, yConstraint])
As mentioned by #dfd in the comments, you should also set this flag:
imageview.translatesAutoresizingMaskIntoConstraints = false
This tells iOS to not create constraints for imageview based upon its frame. If you don't do this, you'll end up with conflicting constraints.
You are going to need width and height constraints for your imageview as well, because you've only specified its position so far.

Related

Change view width constraint

I set up width of UIView like this:
contentView.widthAnchor.constraint(equalToConstant: CGFloat(scrollWidth())).isActive = true
Now I want to change that width. The question is: how to remove old constraint and add new? I did create NSLayoutConstraint like this
scrollWidthConstraint = NSLayoutConstraint(
item: contentView,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 1.0,
constant: CGFloat(scrollWidth())
)
How to set that constraint for UIView for changing later?
First use this better and short and assign a reference to it
var widCon:NSLayoutConstraint!
widCon = contentView.widthAnchor.constraint(equalToConstant: CGFloat(scrollWidth()))
widCon.isActive = true
instead of NSLayoutConstraint(item: contentView, then Change it's constant
widCon.constant = ////

Trying to present tableView

tableView = UITableView(frame: CGRect(x: 0, y: 0, width: view.frame.width/2, height: view.frame.height/2))
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
tableView.dataSource = self
tableView.delegate = self
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: tableView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: tableView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 50.0).isActive = true
I have been trying to figure out how to present this UITableView on my main view but it does not seem to work. Please let me know if you know how to do it. Thank you in advance.
When using auto layout, we usually implement one of these constraint:
Pin 4 edge to superview
Set width, height to a fixed number or percent of superview, then we determine origin point by set x, y or center it horizontally and vertically.
So I recommend you implement two more constraints to TableView's width and TableView's height instead of set it's frame.
Finally, do not forget it it to your root view hierarchy like: self.view.addSubview(tableView)

Swift 4 - How to get bounds of a UIView after Using Constraints Programatically

I really need your help about this issue. I am creating a UIView object programatically without setting the bounds at the beggining. After creating I add constraints to that view in order to fit the required place in the screen.
But the problem is that, I want to use that UIView's bounds property in the following parts of the code but unfortunately I can not get the bounds after constraints sets as it shows on the screen.
Below I have sample code :
let previewView = UIView()
NSLayoutConstraint(item: previewView, attribute: .top, relatedBy: .equal, toItem: upperView, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: previewView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
previewView.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
previewView.translatesAutoresizingMaskIntoConstraints = false
print(previewView.bounds)
// (0.0, 0.0, 0.0, 0.0) ->>>> How can I get the bounds as it shows on the screen instead of 0,0,0,0
After this point, when I try to use, previewView.bounds property, it returns me its original size as 0.
How can I get the bounds as it shows on the screen with the related constaints' setup ?
Thank you very much Guys.
You can access it in
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if once {
once = false
// process here
print(previewView.bounds)
}
}
but first declare the view as instance variable , make once = false in view
var previewView:UIView!
var once= true
see here

Programmatically creating constraints bound to view controller margins

I'm trying to make a view that will act as a sort of "panel", attached to the right side of the view controller.
That is, it is bound to the trailing, top, and bottom margins of the parent view controller, with a static width of 300
However, I just can't seem to get it right, I'm either breaking a constraint or doing something xcode tells me is illegal.
What am I doing wrong?
Here is the code in the controller
let myView = UIView()
view.backgroundColor = UIColor.redColor()
self.view.addSubview(view)
let topConstraint = NSLayoutConstraint(item: myView,
attribute: .Top,
relatedBy: .Equal,
toItem: self.topLayoutGuide,
attribute: .Bottom,
multiplier: 1,
constant: 0)
let trailingConstraint = NSLayoutConstraint(item: self.view,
attribute: .TrailingMargin,
relatedBy: .Equal,
toItem: myView,
attribute: .Trailing,
multiplier: 1,
constant: 0)
let bottomConstraint = NSLayoutConstraint(item: self.bottomLayoutGuide,
attribute: .Top,
relatedBy: .Equal,
toItem: myView,
attribute: .Bottom,
multiplier: 1,
constant: 0)
let widthConstraint = NSLayoutConstraint(item: myView,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 300)
self.view.addConstraints([trailingConstraint])
view.addConstraints([topConstraint, bottomConstraint, widthConstraint])
Actually the problem in your code is that you did not set the translatesAutoresizingMaskIntoConstraints of myview to false, whenever you want to use auto-layout constraints then you have to set translatesAutoresizingMaskIntoConstraints of a view to false.
Another Problem is that you do not add myview on self.view I have updated your code and Its working fine According your constraints.
Put below code in your ViewController .
let myView = UIView()
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Bottom, relatedBy: .Equal, toItem: self.bottomLayoutGuide, attribute:.Top, multiplier: 1, constant: 20))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 300))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .TrailingMargin, relatedBy: .Equal, toItem: view, attribute: .TrailingMargin, multiplier: 1, constant: 0))
In your example code above, it seems like you are mixing up view and myView in a few places. In any event, widthConstraint should be added to myView and topConstraint, trailingConstraint, and bottomConstraint should be added to self.view. The reason for this is constraints must be added to the closest superview ancestor that lays out both views involved in the constraint. In the case where you are constraining a child view attribute to an attribute on its parent view, the constraint must be added to the parent view, since it lays out both itself and the child view. If you have a constraint between two sibling views, the constraint would be added to their parent view, since it is the closest ancestor that lays out both the views involved.
If you're able to target iOS 9.0 and above, it's much cleaner and easier to use the new NSLayoutAnchor and NSLayoutDimension API for creating these kinds of constraints. It also provides strict type checking and the compiler can verify correctness. With these new APIs, your example code would simply become:
let myView = UIView()
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
let margins = self.view.layoutMarginsGuide
myView.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true
myView.topAnchor.constraintEqualToAnchor(margins.topAnchor).active = true
myView.bottomAnchor.constraintEqualToAnchor(margins.bottomAnchor).active = true
myView.widthAnchor.constraintEqualToConstant(300.0).active = true
No need to explicitly add the constraints to the right view, etc. You can read more about this method of creating constraints here:
https://developer.apple.com/library/ios/documentation/AppKit/Reference/NSLayoutAnchor_ClassReference/
and here:
https://developer.apple.com/library/ios/documentation/AppKit/Reference/NSLayoutDimension_ClassReference/
There seems to be some ambiguity in your code, you are creating a UIView as myView but adding view to self.view and even constraint also to view itself. So correct your code and replace view with myView.
Secondly setTranslayesAutoresizingMaskIntoConstraints to false.
Then add all the constraints to self.view. This should solve your problem.
myView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addConstraints([trailingConstraint, bottomConstraint, widthConstraint])
VFL is also a better and clean approach. It actually gives a visualization of how constraint are setup.

Best way to programmatically layout UILabel and UIView subviews in UITableViewCell in Swift

Using Swift and autolayout, what's the best way to put a label and custom view in a cell? I want the label on the left and the view on the right. The view could be any subclass of UIView. One problem I'm having is sizing the label's width to fit the text. I want the custom view to take up the remainder of the space. I prefer to not use the visual format but instead NSLayoutConstraint():
func tableView(aTableView: UITableView, cellForRowAtIndexPath anIndexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(frame: CGRect(x: 0.0, y: 0.0, width: aTableView.bounds.size.width, height: aTableView.estimatedRowHeight))
let titleLabel = UILabel()
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.numberOfLines = 0
titleLabel.text = "some text"
titleLabel.sizeToFit()
cell.contentView.addSubview(titleLabel)
titleLabel.layer.borderColor = UIColor.redColor().CGColor
titleLabel.layer.borderWidth = 1.0
cell.contentView.addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .Left, relatedBy: .Equal, toItem: cell.contentView, attribute: .Left, multiplier: 1.0, constant: 0.0))
let customView = UIView()
cell.contentView.addSubview(customView)
cell.contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .Left, relatedBy: .Equal, toItem: titleLabel, attribute: .Right, multiplier: 1.0, constant: 0.0))
cell.contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .Right, relatedBy: .Equal, toItem: cell.contentView, attribute: .Right, multiplier: 1.0, constant: 0.0))
return cell
}
Try adding the following line:
customView.translatesAutoresizingMaskIntoContraints = false
you might also need an additional constraint to define maybe the max size of the UILabel or the min size of the UIView.
Since you have numberOfLines = 0, the UILabel doesn't know exactly when to break into a new line, since there are no constraints specifying that.
Hope it helps
Create the label and give these constraints
horizontal spacing constraint between custom view and the label
trailing space constraint to content view
Top space and bottom space for the label and custom view
Leading space for the custom view
Here the label automatically grows with the content and the custom view will occupy the remaining space.