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))
Related
I have an UITableView with custom cells. The structure of every cell is like that: I have contentView, in this contentView I have backView (simple UIView with white background and cornered radius 16.0), in this backView I have an imageView with some picture.
What I want is to have this imageView cornered (within his parent UIView — backView — borders). And it doesn't work this way.
The code is quite simple (from ImageCell.swift):
self.backView = UIView()
self.backView.backgroundColor = UIColor.white
self.backView.translatesAutoresizingMaskIntoConstraints = false
self.backView.layer.cornerRadius = 16.0
self.contentView.addSubview(backView)
self.picture = UIImageView()
self.picture.translatesAutoresizingMaskIntoConstraints = false
self.picture.contentMode = UIViewContentMode.scaleAspectFill
self.picture.backgroundColor = UIColor.gray
self.picture.clipsToBounds = true
self.backView.addSubview(picture)
let constraintPicTop = NSLayoutConstraint(item: picture, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .topMargin, multiplier: 1.0, constant: -6)
let constraintPicLeft = NSLayoutConstraint(item: picture, attribute: .left, relatedBy: .equal, toItem: backView, attribute: .leftMargin, multiplier: 1.0, constant: -8)
let constraintPicRight = NSLayoutConstraint(item: picture, attribute: .right, relatedBy: .equal, toItem: backView, attribute: .rightMargin, multiplier: 1.0, constant: 8)
constraintBottomPic = NSLayoutConstraint(item: picture, attribute: .bottom, relatedBy: .lessThanOrEqual, toItem: contentView, attribute: .topMargin, multiplier: 1.0, constant: 150)
I don't know the size of the image beforehand, so constraintBottomPic value is updating in cellForRowAt function.
And it's working except this image is not cornered (and I believe it should be).
(It's not possible for me to set cornerRadius for UIImageView unfortunately).
update: Found the solution. It seems I had to set 'clipsToBounds' to true in all the parent views directly (contentView and backView, in my case).
You should set the clipsToBounds property of the higher level container view (like the contentView of your cell.)
Apply imageView.layer.maskToBounds = YES;
Apply this to view or imageview on which you want to set corner radius.
As you have mentioned corner radius to your view you need to set this for your view
I created a .xib with freeform and implement it like this
if let alertView = Bundle.main.loadNibNamed(Constants.XIB.titleImageLabelThreeButtonsAlertView, owner: self, options: nil)?.first as? TitleImageLabelThreeButtonsAlertView {
view.addSubview(alertView)
alertView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 20))
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 20))
alertView.autoresizingMask = [UIViewAutoresizing.flexibleLeftMargin, UIViewAutoresizing.flexibleRightMargin, UIViewAutoresizing.flexibleTopMargin, UIViewAutoresizing.flexibleBottomMargin]
self.view.layoutIfNeeded()
}
I call this code in viewDidAppear. The center thing seems to work, but it seems that the trailing and leading don't have any effect. I want them with a distance of 20, my alertView should have a fixed height and appear in center.
The xib has always the same size (see screenshots)
My originally targeted was to get a xib that I can implement in every view for every device. So what is the best way to get this?
my xib file
simulator iphone 7
simulator iphone 4
You are mixing up auto layout and fixed placement (with autoresizing mask). What you want to do is completely use auto layout so that the view will adjust its layout automatically. You say you want a horizontal distance of 20, a fixed height and to be centred so I would do this:
if let alertView = Bundle.main.loadNibNamed(Constants.XIB.titleImageLabelThreeButtonsAlertView, owner: self, options: nil)?.first as? TitleImageLabelThreeButtonsAlertView {
view.addSubview(alertView)
// Start using auto layout
alertView.translatesAutoresizingMaskIntoConstraints = false
// Set the leading and trailing constraints for horizontal placement
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: -20))
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 20))
// Centre it vertically
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0))
// Set the fixed height constraint
let fixedHeight: CGFloat = 100
view.addConstraint(NSLayoutConstraint(item: alertView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 0, constant: fixedHeight))
}
That will get you what you want no matter how the device, superview, orientation, etc changes.
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.
Having adopted Autolayout and Storyboards, I'm no longer designing screens at exact dimensions, so when I use images I tend to use a fairly large sized image so that it will look sharp on the iPad but can be scaled down for the smaller iPhones. I drag this image into the Asset Catalog.
When I place the image into a View Controller in my Storyboard, the image wants to be its intrinsic size. I can only get it to show smaller by setting a specific width, proportional width to another object, or by constraining it between two other items.
I'm now at another point of the project where I need to add the image in code. Although I am setting a specific height and width for the image's frame, the image still appears on screen at its intrinsic size and not where I have set it using:
myImage.frame = CGRect(x: self.view.center.x - 50, y: 100, width: 100, height: 100)
Any ideas on what I'm missing? Thanks!
I look you want add an imageView in CenterX and margin top 100. And hold image aspect in square size 100, 100. This code below will help you.
let imageName = "your image name in resource"
let imageView = UIImageView(frame: CGRectMake(0, 0, 200, 200))
imageView.image = UIImage(named: imageName)
imageView.contentMode = .ScaleAspectFit
imageView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(imageView)
// add your constraint
let constTop = NSLayoutConstraint(item: imageView, attribute:.Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 100)
// var constLeading = NSLayoutConstraint(item: imageView, attribute: .Leading, relatedBy: .Equal, toItem: self.view, attribute: .Leading, multiplier: 1, constant: self.view.center.x - 50)
var constCenterX = NSLayoutConstraint(item: imageView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0);
var constWidth = NSLayoutConstraint(item: imageView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .Width, multiplier: 1, constant: 100);
var constHight = NSLayoutConstraint(item: imageView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 1, constant: 100);
imageView.addConstraints([constHight, constWidth])
self.view.addConstraints([constTop, constCenterX])
Hope this helps!
You need to set the UIImageView's contentMode which is actually a property of UIView:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIView_Class/index.html#//apple_ref/swift/enum/c:#E#UIViewContentMode
If you take on layout in code, I hope you're doing it in the right place... by overriding layoutSubviews(), and if that needs to get triggered by an external event, that the event fires setNeedsLayout() on the parent view.
Lots of handshaking and configuring to do if it must be done in code.
This is my first iOS app and I'm using Swift. I'm following tutorials on CodeWithChris.com.
The user will be tapping on a series of icons to determine what device model they have. So far I've used a handful of constraints to get the icons placed properly on an iPhone 6 display. When using iPhone 5S/5c/5 or 4S/4 display sizes the icons and text still render at the full size for iPhone 6 and they go off the screen.
Here's my process for building the layout:
Grab and parse JSON to NSArray. Returns ["iPhone", "Android", "Windows", "iPad"]
Create UIView object for each item in NSArray. Set Name variable to the associated name.
Place each UIView using addSubview()
Apply height, width, and bottom-margin constraints to the UIView
Set UIView position constraints based on its position in the row
Add the icons (UIImageViews) to each UIView
Set height and width constraints based on the type of device
Add text labels
I'm using a bunch of constraints to place the UIViews and the UIImageViews. How can I make these constraints dynamic based on the device's screen size?
iPhone 6 with contentView in blue and UIViews in red
iPhone 6
iPhone 5/5S/5c
iPhone 4S/4
Here's where I add the device UIViews and apply constraints:
// Loop through the array of DeviceTypes to add each to the UIView
for index in 0...deviceTypes.count-1 {
// Grab the current device
var thisDevice:DeviceType = deviceTypes[index]
// Add to the view!
contentView.addSubview(thisDevice)
thisDevice.setTranslatesAutoresizingMaskIntoConstraints(false)
// How tall is the Device UIView
var heightConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 200)
// How wide is the device UIView
var widthConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 130)
// How far is the bottom of the UIView from the top of the contentView
var bottomMarginConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 100)
// Add thisDevice's UIView constraints
thisDevice.addConstraints([heightConstraint, widthConstraint])
contentView.addConstraint(bottomMarginConstraint)
thisDevice.backgroundColor = UIColor.redColor()
// set UIView position constraints based on place in row
if (index > 0) {
// device is second or further in row
var deviceOnTheLeft = deviceTypes[index-1]
var leftMarginConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: deviceOnTheLeft, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 10)
contentView.addConstraint(leftMarginConstraint)
}
else {
// device is first in row
var leftMarginConstraint:NSLayoutConstraint = NSLayoutConstraint(item: thisDevice, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0)
// Add constraint
contentView.addConstraint(leftMarginConstraint)
}
Here are the constraints that I apply to the icon images:
func addDeviceImages() {
// Set right image based on deviceType name
self.frontImageView.image = UIImage(named: self.Name!)
// Set translates autoresizing mask to fault
self.frontImageView.setTranslatesAutoresizingMaskIntoConstraints(false)
// Add the imageview to the view
self.addSubview(self.frontImageView)
if (self.Name == "Windows") {
self.addImageSizeConstraints(90, height: 160)
}
else if (self.Name == "iPad"){
self.addImageSizeConstraints(120, height: 180)
}
else if (self.Name == "iPhone"){
self.addImageSizeConstraints(85, height: 175)
}
else if (self.Name == "Android"){
self.addImageSizeConstraints(95, height: 185)
}
}
func addImageSizeConstraints(width: CGFloat, height: CGFloat) {
// Set the size constraints for the imageview based on the values passed in
var heightConstraint:NSLayoutConstraint = NSLayoutConstraint(item: self.frontImageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: height)
var widthConstraint:NSLayoutConstraint = NSLayoutConstraint(item: self.frontImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: width)
self.frontImageView.addConstraints([heightConstraint, widthConstraint])
// Set the position of the imageview in the UIView
var verticalConstraint:NSLayoutConstraint = NSLayoutConstraint( item: self.frontImageView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: -25)
var horizontalConstraint:NSLayoutConstraint = NSLayoutConstraint( item: self.frontImageView, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 5)
self.addConstraints([horizontalConstraint,verticalConstraint])
}
You are using the wrong kinds of constraints. Your width constraints are absolute - you are setting a fixed constant. Instead, make the constant 0, and the widths depend on the height, using a value for the multiplier that fixes the aspect ratio correctly. In this way, as the height changes, the width will change to match. Do the same sort of thing with the separation between the images - make the separation depend on the width of the superview, as a multiplier.