Issue setting frame for UIImageView before applying constraints - ios

I'm coming across an issue with the following lines of code:
let imageName = "smiley.png"
let imageU = UIImage(named: imageName)
let imageView = UIImageView(image: imageU)
imageView.frame = CGRect(x: 5.0, y: 5.0, width: 5.0, height: 5.0)
contentView!.addSubview(imageView)
let imageTopConstraint = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.TopMargin, relatedBy: NSLayoutRelation.Equal, toItem: bodyText, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 5)
let imageLeftConstraint = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.LeftMargin, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 16)
imageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activateConstraints([imageTopConstraint, imageLeftConstraint])
When I run this code, I am getting a gigantic smiley (probably 150x150) and the constraints don't seem to be working correctly either. Is there something wrong with setting the frame and then applying constraints? What is the best practice when creating and constraining views programmatically? Thanks, and apologies for the semi-vague question.

Related

navigationItem.titleView can't render at the center when there are more than one navigationItem.rightBarButtonItem?

I'm doing this tutorial. I have to fix my code because of this problem. Navbar is rendering as following image. I just need my title icon to be at the center of the nav bar. Any tricks?
Here is my current code.
func setUpNavigationBarItems(){
//https://www.youtube.com/watch?v=zS-CCd4xmRY
let titleImageView = UIImageView(image: UIImage(named: "ic_nav_app_icon"))
titleImageView.frame = CGRect(x: 0, y: 0, width: 34, height: 34)
titleImageView.contentMode = .scaleAspectFit
navigationItem.titleView = titleImageView
let addButton = UIButton(type: .system)
let addImage = UIImage(named: "ic_nav_add")
addButton.setImage(addImage?.withRenderingMode(.alwaysOriginal), for: .normal)
NSLayoutConstraint(item: addButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
NSLayoutConstraint(item: addButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: addButton)
let searchButton = UIButton(type: .system)
let searchImage = UIImage(named: "ic_nav_phone")
searchButton.setImage(searchImage?.withRenderingMode(.alwaysOriginal), for: .normal)
NSLayoutConstraint(item: searchButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
NSLayoutConstraint(item: searchButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
let settingButton = UIButton(type: .system)
let settingImage = UIImage(named: "ic_nav_setting")
settingButton.setImage(settingImage?.withRenderingMode(.alwaysOriginal), for: .normal)
NSLayoutConstraint(item: settingButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
NSLayoutConstraint(item: settingButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
navigationItem.rightBarButtonItems = [UIBarButtonItem(customView: searchButton), UIBarButtonItem(customView: settingButton)]
navigationController?.navigationBar.backgroundColor = .white
}
It should have the correct layout if you replace:
titleImageView.frame = CGRect(x: 0, y: 0, width: 34, height: 34)
With:
NSLayoutConstraint(item: titleImageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 34).isActive = true
NSLayoutConstraint(item: titleImageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 34).isActive = true
As explained in the previous answer the frame of the title view is likely not 34x34 at runtime. Instead it is being partly determined by the size of the image (the intrinsic content size of the UIImageView depends on the size of the image) and the Auto Layout configuration of the UINavigationBar.
If you run the view debugger, you might see that the frame of the title view is something like 150x44, which is why it is being offset to one side to make space for everything in the UINavigationBar.
The view debugging tool is located in the bottom bar inside Xcode (on top of the debug area):
It lets you inspect frames, constraints and more of the view hierarchy and will often give you a hint as to what might be wrong when facing issues like this one.

Adding leading constraint programmatically crashes app

I'm trying to get my head around how adding constraints programmatically works. So far I have my code like so:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//addViewStandard()
addConstraintsView()
}
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
// I want to mimic a frame set of CGRect(x: 20, y: 50, width: 50, height: 50)
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 20)
someView.translatesAutoresizingMaskIntoConstraints = false
someView.addConstraints([widthConstraint, heightConstraint, leadingConstraint])
view.addSubview(someView)
}
}
Now when I run the app it crashes because of the leading constraint. The error message is "Impossible to set up layout with view hierarchy unprepared for constraint". What am I doing wrong here? Should I be adding the constraints to the object (the blue box on this case) or adding them to its superview?
EDIT:
After code changes I have:
func addConstraintsView() {
let someView = UIView(frame: CGRect.zero)
someView.backgroundColor = UIColor.blue
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
let widthConstraint = NSLayoutConstraint(item: someView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let heightConstraint = NSLayoutConstraint(item: someView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
let leadingConstraint = NSLayoutConstraint(item: someView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 20)
someView.addConstraints([widthConstraint, heightConstraint])
view.addConstraints([leadingConstraint])
}
First of all,
view.addSubview(someView)
someView.translatesAutoresizingMaskIntoConstraints = false
should come before the constraints phase; you have to apply the constraints AFTER someView is added to its superview.
Also, if you are targeting iOS 9, I'd advise you to use layout anchors like
let widthConstraint = someView.widthAnchor.constraint(equalToConstant: 50.0)
let heightConstraint = someView.heightAnchor.constraint(equalToConstant: 50.0)
let leadingConstraint = someView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, leadingConstraint])
This way you don't have to worry about which view to apply the constraints to.
Finally (and to clear up your doubt), if you can't use layout anchors, you should add the leading constraint to the superview, not the view.

Using Autolayout Constraints programmatically to center a UIImageView inside a UIView Swift 2.3 iOS10

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.

UIView with gradient layer not conforming to constraints set

In viewDidLoad, I instantiate a UIView and add a gradient layer to that UIView.
This works well on iPhone, but on iPad, UIView does not stretch out to fill the entire screen. Please note the following screenshot for iPad sim.
iPad simulator screenshot
I have attempted adding constraints both programmatically and by using XCode.
It seems that once I add the CAGradientLayer, the UIView does not conform to the constraints that I set, either programmatically or using XCode tools.
Sample Code:
gradientLayer = CAGradientLayer()
gradientLayer.frame = self.view.bounds
gradientLayer.locations = [0.5, 1.0]
gradient_View.backgroundColor = UIColor.blueColor()
let color1 = self.opus_Page_Background_Color.CGColor as CGColorRef
let color4 = self.opus_Page_Tertiary_Color.CGColor as CGColorRef
gradientLayer.colors = [color1, color4]
self.gradient_View.frame = self.view.bounds
self.gradient_View.layer.insertSublayer(gradientLayer, atIndex: 0)
gradient_View.translatesAutoresizingMaskIntoConstraints = false
let leadingConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
leadingConstraint.active = true
view.addConstraint(leadingConstraint)
let trailingConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0)
trailingConstraint.active = true
view.addConstraint(trailingConstraint)
let widthConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)
view.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)
view.addConstraint(heightConstraint)
NSLayoutConstraint.activateConstraints([leadingConstraint, trailingConstraint, widthConstraint, heightConstraint])
Try changing the last two constraints. Instead of matching width and height, pin it to Top & Bottom. The problem probably is that in viewDidLoad, your view has width = X, height = Y, but that's not the final layout.
If that does not work, try adding it in viewDidLayoutSubviews.
Hope it helps!
You have added leading and trailing constraint which means you have got the width of the view. So there is no need to add width constraint at all. Remove width from storyboard or programmatically which ever you are using.
Also i could see a missing top constrai nt. Resolve that also

UIImageView does not follow the constraints that I applied

I have a parent, called infoView. It has two children: subLabel and tinyImageView. I'd like both of these children to be size 30.0, with subLabel first, followed by 10 pixel padding, followed by the tinyImageView.
For some reason, my tinyImageView is not respecting ANY of the constraints I put below. Even the height/width is not respected.
let boxSize = infoView.frame.size.height //30
let subLabel = UILabel()
subLabel.frame = CGRectMake(0, 0, boxSize, boxSize)
subLabel.layer.cornerRadius = 5.0
subLabel.clipsToBounds = true
subLabel.backgroundColor = logoColor(1)
subLabel.text = String(post.subscribers)
subLabel.font = UIFont(name: "Lato-Bold", size: 13.0)
subLabel.textColor = UIColor.whiteColor()
subLabel.textAlignment = .Center
infoView.addSubview(subLabel)
//Image
let tinyImageView = UIImageView(image:UIImage(named: "MO.jpg"))
tinyImageView.layer.cornerRadius = 2.5
tinyImageView.clipsToBounds = true
infoView.addSubview(tinyImageView)
let widthConstraint = NSLayoutConstraint(item: tinyImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: boxSize)
tinyImageView.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: tinyImageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: boxSize)
tinyImageView.addConstraint(heightConstraint)
print("------------")
let horizontalConstraint = NSLayoutConstraint(item: subLabel, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: tinyImageView, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 10)
infoView.addConstraint(horizontalConstraint)
let verticalConstraint = NSLayoutConstraint(item: subLabel, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: tinyImageView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
verticalConstraint.active = true
The image turns out way too big, and the horizontal/vertical constraints don't work at all. Currently, the image overlaps subLabel, as if it was just added without any constraints.
As #Paulw11 stated in the comments, you need to set translatesAutoResizingMaskIntoConstraints to false for programmatically created views. Also, your constraints will resize the frame for your UIImageView, but they won't scale the image. For that, you need to set the contentMode property:
tinyImageView.translatesAutoResizingMaskIntoConstraints = false
tinyImageView.contentMode = .ScaleToFill
Values you probably want to consider for contentMode are .ScaleToFill (scale to fit area distorting aspect ratio if necessary), .ScaleAspectFit (maintain aspect ratio and leave part of the view as transparent if necessary) and .ScaleAspectFill (maintain aspect ratio and clip part of image if necessary).
From your code, I just saw one activation for constraint verticalConstraint.
active all the constraints you add.
if your constraints got conflicted,
Set translatesAutoResizingMaskIntoConstraints to false(usually if you set constraints programmatically, you set it to false)

Resources