Currently I am using this line of code to set the origin on a UILabel at the top left corner...
addConstraint(NSLayoutConstraint(item: messageLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
However I want the origin to be 0 pixels from left edge H and half of height if that makes sense.
Any suggestions? I've tried changing the multiplier to no avail.
What I am trying to do is move the origin so that (0,0) instead of being the top left corner, is the furthermost left of the label and half of the label height so that I can center the label properly.
Just to clarify, you need the label left aligned and centered top to bottom?
Have you looked at NSLayoutAnchor?
messageLabel = UILabel(/* Initialized somehow */)
messageLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(messageLabel)
view.centerYAnchor.constraint(equalTo: messageLabel.centerYAnchor).isActive = true
view.leadingAnchor.constraint(equalTo: messageLabel.leadingAnchor).isActive = true
try to set the origin of your label with this:
messageLabel.horizontalAlignmentMode = .Left
messageLabel.position = CGPoint(x:0.0, y:self.size.height)
no need for constraint!!
Hope that help you out :)
First you have to add your view as a subview of its container. Once you have done this you will want to set the translatesAutoResizingMaskToConstraints to false. Then it is time to add your NSLayoutConstraints:
let labelWidth:CGFloat = 320
let labelHeight:CGFloat = 30
// Container is what you are adding label too
let container = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
let label = UILabel(frame: CGRect(x: 0, y: (container.frame.size.height / 2) - (labelHeight / 2), width: labelWidth, height: labelHeight))
container.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
// Add Width and Height (IF is required,
// if not required, anchor your label to appropriately)
label.addConstraints([
NSLayoutConstraint.init(item: label, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: labelWidth),
NSLayoutConstraint.init(item: label, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: labelHeight),
])
label.layoutIfNeeded()
// Add left constraint and center in container constraint
container.addConstraints([
NSLayoutConstraint.init(item: label, attribute: .left, relatedBy: .equal, toItem: .container, attribute: .left, multiplier: 1.0, constant: 0.0),
NSLayoutConstraint.init(item: label, attribute: .centerX, relatedBy: .equal, toItem: .container, attribute: .left, multiplier: 1.0, constant: 0.0)
])
container.layoutIfNeeded()
EDIT
Yes there is documentation given to you automatically by Xcode when you use dot syntax. If you go to the parameter of the attribute and just enter a period, you will see a drop down of all the possible options.
Related
I am trying to put a UIActivityIndicatorView inside each collection view cell as it downloads its image. I have it appearing in each cell, but it refuses to center itself. It stays in the top left corner. How can I get it to center itself properly?
Here's how I'm doing it:
extension UIView {
func showActivityIndicator(onView: UIView, withIndicator: UIActivityIndicatorView) {
withIndicator.frame = CGRect(x: onView.frame.midX - 20, y: onView.frame.midY - 20, width: 40, height: 40)
withIndicator.activityIndicatorViewStyle = .whiteLarge
withIndicator.center = onView.center
onView.addSubview(withIndicator)
withIndicator.startAnimating()
}
}
I call that function inside cellForItemAtIndexPath like:
showActivityIndicator(onView: cell.contentView, withIndicator: activityInd)
But nothing I do will move it from the top left corner. Any advice?
Try this
withIndicator.center = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
You need to add contraints to center it. For example use NSLayoutAnchor.
You need to set translatesAutoresizingMaskIntoConstraints to false, to set your constraints. And after adding it to the view set the constraints (hints in the code comments):
extension UIView {
func showActivityIndicator(onView: UIView, withIndicator: UIActivityIndicatorView) {
withIndicator.frame = CGRect(x: onView.frame.midX - 20, y: onView.frame.midY - 20, width: 40, height: 40)
withIndicator.activityIndicatorViewStyle = .whiteLarge
// set translatesAutoresizingMaskIntoConstraints to false to set your constraints
withIndicator.translatesAutoresizingMaskIntoConstraints = false
onView.addSubview(withIndicator)
withIndicator.startAnimating()
// add the constraints to center the indicator
withIndicator.centerXAnchor.constraint(equalTo: onView.centerXAnchor).isActive = true
withIndicator.centerYAnchor.constraint(equalTo: onView.centerYAnchor).isActive = true
}
}
I suggest using constraints (aka auto layout):
indicator.translatesAutoresizingMaskIntoConstraints = false
"your cell".addSubview(indicator)
let widthConstraint = NSLayoutConstraint(item: indicator, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 250)
let heightConstraint = NSLayoutConstraint(item: indicator, attribute: .Height, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 100)
let xConstraint = NSLayoutConstraint(item: indicator, attribute: .CenterX, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: indicator, attribute: .CenterY, relatedBy: .Equal, toItem: self.tableView, attribute: .CenterY, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([widthConstraint, heightConstraint, xConstraint, yConstraint])
I have added a UIView to a table cell through my storyboard with the following constraints:
Then I have the following code to programmatically add a UIImageView to the UIView above and size it according to the orientation of the screen.
//Use half the screen size width when on an iPhone and on Landscape
let image: UIImage = UIImage(named: HEADER_IMAGE_BATH)!
imageView = UIImageView(image: image)
imageView!.frame = CGRectMake(0 , 0, self.view.frame.width / 2, 185)
imageView!.contentMode = .ScaleAspectFit
//center image
let centerXConst = NSLayoutConstraint(item: imageView!, attribute: .CenterX, relatedBy: .Equal, toItem: imageWrapperView, attribute: .CenterX, multiplier: 1, constant: 1)
let centerYConst = NSLayoutConstraint(item: imageView!, attribute: .CenterY, relatedBy: .Equal, toItem: imageWrapperView, attribute: .CenterY, multiplier: 1, constant: 1)
NSLayoutConstraint.activateConstraints([centerXConst, centerYConst])
//add to sub view
imageWrapperView.addSubview(imageView!)
However, my image does not get centered when in landscape. My image is only half the width of the screen and I would like to center it inside my UIView. What am I missing? Thanks
You should use Auto Layout instead of frame for width and height of imageView
You have to add imageView to imageWrapperView before add constraints
You have to set imageView.translatesAutoresizingMaskIntoConstraints to false
Then, the final code is:
//Use half the screen size width when on an iPhone and on Landscape
let image: UIImage = UIImage(named: "key.png")!
let imageView = UIImageView(image: image)
imageView.contentMode = .ScaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
//add to sub view
imageWrapperView.addSubview(imageView)
//center image
let centerXConst = NSLayoutConstraint(item: imageView, attribute: .CenterX, relatedBy: .Equal, toItem: imageWrapperView, attribute: .CenterX, multiplier: 1.0, constant: 0.0)
let centerYConst = NSLayoutConstraint(item: imageView, attribute: .CenterY, relatedBy: .Equal, toItem: imageWrapperView, attribute: .CenterY, multiplier: 1.0, constant: 0.0)
let heightConstraint = NSLayoutConstraint(item: imageView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 185.0)
let widthConstraint = NSLayoutConstraint(item: imageView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: self.view.frame.width / 2)
imageView.addConstraints([heightConstraint, widthConstraint])
NSLayoutConstraint.activateConstraints([centerXConst, centerYConst])
When you're using autolayout, do not try to change the frame of the views. If you need to change the frame, better take the outlets of the constraints and change them programatically.
In your question, to center align the imageView, you can do it by putting 4 constraints. Set the height and width of the imageView and the other two are center horizontally and center vertically constraints.
This is the basic way of center aligning any type of view.
I am creating a UIButton programmatically in the code and am constraining it to the top and the right of the ViewController. When the ViewController loads, the UIButton is in the correct position on the screen. The problem comes when I rotate the device, the constraints don't seem to have taken. Here is the code I use to create the UIButton:
let xValue = UIScreen.mainScreen().bounds.width - 44
let closeButton = UIButton(frame: CGRect(origin: CGPoint(x: xValue, y: 0), size: CGSize(width: 44, height: 44)))
closeButton.setImage(UIImage(named: "close"), forState: .Normal)
closeButton.addTarget(self, action: "closeClicked", forControlEvents:.TouchUpInside)
self.view.addSubview(closeButton)
self.view.addConstraint(NSLayoutConstraint(item: closeButton, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: closeButton, attribute: .Right, relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1, constant: 0))
Is there something else I need to do to ensure the constraints are applied after an orientation change?
You shouldn't combine frames with constraints. By default, iOS will convert your frame to constraints, and that is causing a conflict with the constraints you are setting. After creating your button, you need to call:
For Swift 1.2:
closeButton.setTranslatesAutoresizingMaskIntoConstraints(false)
For Swift 2.0:
closeButton.translatesAutoresizingMaskIntoConstraints = false
so that iOS doesn't try to convert your frame into constraints. Then you need to add two additional constraints for the width and height of your button:
closeButton.addConstraint(NSLayoutConstraint(item: closeButton, attribute: .Width,
relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 44))
closeButton.addConstraint(NSLayoutConstraint(item: closeButton, attribute: .Height,
relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 44))
Since your button is no longer using the frame, you can create it with:
let closeButton = UIButton()
Now your button will stick to the upper right of your screen in all orientations for all devices.
All You need is to disable animation (uncheck "Animation" checkbox) on segue and then will be no need to update constraints.
Can be found in Segue options in right panel while editing storyboard.
I am trying to move a label with an animation but I want it to be between two other objects. I thought I could do it with NSLayoutConstraints like
let labelLeftConstraint = NSLayoutConstraint(item: self.label, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: self.leftObject, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0)
let labelRightConstraint = NSLayoutConstraint(item: self.label, attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.LessThanOrEqual, toItem: self.rightObject, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0)
let labelBottom = NSLayoutConstraint(item: self.label, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.bottomObject , attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
let labelCenterX = NSLayoutConstraint(item: self.label, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.movingObject, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
label.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(label)
self.view.addConstraints([labelLeftConstraint, labelRightConstraint, labelBottom, labelCenterX])
And I am trying to update the position with
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
self.movingObject.center = CGPoint(x: newX, y: newY)
self.view.updateConstraints()
}
But when the movingObject is moving too far it crops the label (which obviously makes sense because of the labelCenterX should be equal to the movingObject). How could I prevent this and instead move it only up to the edges of the leftObject and rightObject? I would need something like .LessEqualOrGreater as for the relation...
I also tried to animate the label also with the .center-method but I didn't find a way to prevent it from getting out of the two objects.
Thank You for your Help (I know I am probably asking a dumb question but I am just learning Swift)
Edit:
Here is a Screenshot of my problem:
The movingObject is the picture of bike which moves with changing velocity from left to right and is allowed to go further than the label with the velocity over the bar. This label should only be between the 0 and the 30-Label
Edit 2:
The working code is in my Git repository
The solution is to create required leading/trailing constraints >= 0 and a centerX constraint with a lower priority, then changing a centerX constant to whatever you need.
I would like to implement a UIImageView programmatically but it's not working quite like I expected when I apply auto layout constraints. For simplicity, I've set the frame of the image view to be 100 x 100. This works well, but once I add auto layout constraints the frame size is no longer respected and the image is rendered at its full size (in this case, 320px wide) instead of scaling down to fit within the image view frame.
Why is that and how can I obtain the desired behavior? I wanted a 100x100 image view that would scale the image down respecting the aspect ratio, and located 50 up from the bottom centered in the middle of the screen.
let imageView = UIImageView(frame: CGRectMake(0, 0, 100, 100))
imageView.image = UIImage(named: "myimg")
imageView.contentMode = .ScaleAspectFit
imageView.clipsToBounds = true
imageView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(imageView)
self.view.addConstraint(NSLayoutConstraint(item: imageView, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: -50))
self.view.addConstraint(NSLayoutConstraint(item: imageView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0))
You should not set any frames when using auto layout. You need to add two more constraints for the width and height of the image view.
let imageView = UIImageView()
// your other code here
imageView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 100))
imageView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 100))