UIImageView size relative to screen size - ios

I have an UIImageView, with an image like so
imageView.image = UIImage(named: "imageName")
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
On the iPhone X this image looks great with 140x140 dimensions
Maybe needless to say, this does not look great on the iPhone5s, since 140x140 would take up too much of screen space
How can I correctly scale my image for other screen dimensions, what would be the best practice for this?
As for now, I set the image height and width to
let size = UIScreen.main.bounds.height / 5.5
imageView.heightAnchor.constraint(equalToConstant: size),
imageView.widthAnchor.constraint(equalToConstant: size),
Since that will be pretty close to what I am after. But I can't shake the feeling that there are better alternatives than this

The question is a bit broad like "What do you think guys?", but still.
It's better to change your size accordingly to your parent view, not screen.
imageView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2, constant: 0)
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 1.0, constant: 0)

Related

Swift issue when set an image as background programmatically

I'm trying to set an image as background to the whole controller so I've written the code which I enclosed below and works fine on iPhone 11 Pro Max.
But surprisingly that wouldn't work as expected on iPhone X and the image was cut off at the bottom of the screen so I tried to modify UIImageView contentMode but there was no result.
Please take a look at the screenshot to see the simulators:
Here is my code:
let img = UIImageView(frame: holderView.bounds)
img.image = UIImage(named: "step 2")
img.contentMode = .scaleToFill
holderView.addSubview(img)
The holderView was pinned in Interface Builder into the fourth edges of superview.
Any ideas to fix it but I don't want to use auto-layout here because this is a simple case to illustrate the issue and it would be a complex one.
You should set the contentMode to scaleToFill if you want to display the entire image irrespective of the displayed device. Also, constraint the UIImageView to it's superview UIView.
let img = UIImageView(frame: holderView.bounds)
img.contentMode = .scaleToFill
img.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
img.topAnchor.constraint(equalTo: view.topAnchor),
img.leadingAnchor.constraint(equalTo: view.leadingAnchor),
img.bottomAnchor.constraint(equalTo: view.bottomAnchor),
img.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])

UIImageView gets pixelated

I need an image in two different sizes. 100px and 50px
I exported it with a height of 100px
While using the scaleAspectFit contentMode the smaller imageView gets pixelated.
According to this answer the solution would be to scale the image before importing it into the project. Since I need 2 different sizes I would prefer an alternative where I could only include the higher size and scale it down for the smaller imageView in order to keep the project size smaller. Is there any solution to achieve non pixelated images?
let templateImage = UIImage(named: exercise.imageName)?.withRenderingMode(.alwaysTemplate)
imageView.image = templateImage
imageView.tintColor = UIColor.blue
imageView.contentMode = .scaleAspectFit

Swift 3 - Can not get label - creating in code - to match width when rotating to landscape mode

I created a UILabel in my Swift 3 code (I have some labels on storyboard and those resize just fine when rotating, it is just the ones I create in swift code that don't resize so clearly I am doing it wrong). I want the width to match the width of the view it is in. When I create it, it sizes correctly but when the view is rotated to landscape mode, it does not resize. I started by adding NSLayoutConstraint for the right of the label to match the right side of the view, but I kept having issues. After diving into the documentation, I found that it is safer and more appropriate to use NSLayoutAnchor to perform this task. The doc says it can catch more autolayout issues at compile time. I found that to be the case and it is a but easier to understand and use. But, it still does not work. When the view is rotated, the label stays the same width.
// Swim Title
newXPosition = 4
label = UILabel(frame: CGRect(x: newXPosition, y: newYPosition, width: labelParentView.frame.width - 4, height: SPLIT_HEIGHT))
label.textAlignment = .left
label.text = "Swim Splits"
label.backgroundColor = UIColor.lightGray
self.labelParentView.addSubview(label)
// Creating the same constraints using Layout Anchors
let margins = labelParentView.layoutMarginsGuide
label.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
labelParentView.layoutIfNeeded()
Two ways to do this...
A. Tell the label to use an autoresizing mask:
self.labelParentView.addSubview(label)
// use autoresizing mask
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// don't need anything else
//let margins = labelParentView.layoutMarginsGuide
//label.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
//labelParentView.layoutIfNeeded()
or, B. Turn off autoresizing mask, and add constraints:
self.labelParentView.addSubview(label)
// turn off autoresizing mask
label.translatesAutoresizingMaskIntoConstraints = false
// add constraints
label.leftAnchor.constraint(equalTo: labelParentView.leftAnchor, constant: 4.0).isActive = true
label.topAnchor.constraint(equalTo: labelParentView.topAnchor, constant: 4.0).isActive = true
label.rightAnchor.constraint(equalTo: labelParentView.rightAnchor, constant: -8.0).isActive = true
label.heightAnchor.constraint(equalToConstant: SPLIT_HEIGHT).isActive = true

Swift 3 | Button Label under Image AND resize Image

I'm displaying an image on top of a button, everything is OK on iPhone 7 e.g. :
I set button background color to blue to see the button frame.
Since I set an aspect ratio on my button's parent view, the button size change on small device, and on iPhone 4S, I have :
The button's images are not resized.
This is my code :
public extension UIButton {
func setButtonWithTextBehindImage () {
let spacing = CGFloat(0.0)
let imageSize = self.imageView?.frame.size
self.titleEdgeInsets = UIEdgeInsetsMake(0.0, -(imageSize?.width)!, -((imageSize?.height)! + spacing), 0.0)
let titleSize = self.titleLabel?.frame.size
self.imageEdgeInsets = UIEdgeInsetsMake(-((titleSize?.height)!), 0.0, 0.0, -(titleSize?.width)!)
}
}
I tried to set contentMode .scaleAspectFit on button and on button imageView but everytime my imageSize is (30, 30) (the size of my #1x image) and it doesn't resize to button size.
How can I do ? TY
Could you provide a bit more context on how you're using the code snippet you provided (maybe some code that we could try running)?
Documentation from https://developer.apple.com/reference/uikit/uibutton/1624010-titleedgeinsets mention insets being applied after the rectangle has been sized to fit. The time at which you're calling your function in the extension might have an effect on the expected resize not happening - as in, are you calling this when the view is loaded, after it appears, after subviews have been laid out.

iOS swift Autolayout constraint for iPhone 4s [duplicate]

I'm tuning my UI App, but I got an issue that I can't solve.
As I can see Compact height affects all iPhones under 4.7 inches, but my UI is fine except for the iPhone 4S (3.5 inches).
I don't want to modify the layout for all iPhones under 4.7 inches, just the iPhone 4S, at the same time I don't want to left out this device.
There's any workaround so I can set the amendments but just and only for the 3.5 inches portrait? or should I say goodbye to 100 millions devices out there?
I know it's a tough question and almost an opinion poll, but technically speaking I would like to find my best way out here.
There is no size class for iPhone 3.5 inch.
So I've made a class category for NSLayoutConstraint to edit it in Interface Builder which is very easy to use:
#interface NSLayoutConstraint (Extensions)
#property (nonatomic) IBInspectable CGFloat iPhone3_5_Constant;
#end
–
#implementation NSLayoutConstraint (Extensions)
- (CGFloat)iPhone3_5_Constant
{
return self.constant;
}
- (void)setIPhone3_5_Constant:(CGFloat)iPhone3_5_Constant
{
if ([UIScreen mainScreen].bounds.size.height < 500) {
self.constant = iPhone3_5_Constant;
}
}
#end
An approach that just worked for me was to use the same constraints for all compact size classes but to use a combination of a greater than or equal to constraint and priorities to modify how the views were positioned on the iPhone 4's smaller screen.
I've got a constraint between the top of a numeric keypad view and its superview that is set to be greater than or equal to 160 (with a priority of 1000) and a constraint between the bottom of the keypad view and the bottom of the superview that is set to a constant of 30 (but with a lower priority of 750).
This means that on the iPhone 4 where there's not enough room for 160+ points above the keypad and 30 points below then it's the space below that goes.
Whilst this approach may not work in all cases, I'd encourage you to think about whether there's a set of priorities that will allow your views to adjust in the way you want on the smaller screen.
Swift 3 version of Pavel Alexeev's solution. In Swift you can't use stored properties in extensions, so we apply it directly to the constant property.
extension NSLayoutConstraint
{
//We use a simple inspectable to allow us to set a value for iphone 4.
#IBInspectable var iPhone4_Constant: CGFloat
{
set{
//Only apply value to iphone 4 devices.
if (UIScreen.mainScreen().bounds.size.height < 500)
{
self.constant = newValue;
}
}
get
{
return self.constant;
}
}
}
#all I can't make things smaller, because I'm dealing with pickerviews which happen to have only three valid heights for UIPickerView (162.0, 180.0 and 216.0). Sizes and constraints apart.
iPhone Sizes: http://www.idev101.com/code/User_Interface/sizes.html , 4S is unique.
So although my approach it's a little bit ugly get the things done, nearly on my point.
So I know it's far from Goodville, don't hit me down, just for sharing:
func checkForiPhone4S()
{
if (UIScreen.mainScreen().bounds.size.height == 480) {
println("It is an iPhone 4S - Set constraints")
listPickerView.transform = CGAffineTransformMakeScale(1, 0.8);
var constraintHeight = NSLayoutConstraint(item: listPickerView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)
self.view.addConstraint(constraintHeight)
datePickerView.transform = CGAffineTransformMakeScale(1, 0.8);
var constraintHeightDate = NSLayoutConstraint(item: datePickerView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)
self.view.addConstraint(constraintHeightDate)
}
}
Swift 5.0 code for Pavel Alexeev's solution., accounting for some syntax updates and also screen width because I've found that if the device is being held in the landscape orientation when the app is launched, the screen height is not the actual portrait height, but the current landscape height. So, I check that the width is accounted for, too. If the height is less than 660 AND the width is less than 375, we have a portrait SE or 5s.
extension NSLayoutConstraint
{
//We use a simple inspectable to allow us to set a value for iphoneSE / 5s.
#IBInspectable var iPhoneSE_PortraitConstant: CGFloat
{
set{
//Only apply value to iphone SE and 5s devices.
if (UIScreen.main.bounds.size.height < 660 && UIScreen.main.bounds.size.width < 330)
{
self.constant = newValue;
}
}
get
{
return self.constant;
}
}
}

Resources