Setting UIImageView Width, Height, Aspect Ratio & Constraints X, Y - swift

I've set a UIImageView in my view controller and assigned constraints x & Y which works I'm trying to set the image width, height & aspect ratio but it doesn't seem to be working.
I've tried many answers already on here but can seem to get it working.
import UIKit
class GuestLoginViewController: UIViewController {
let headerImage = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
setHeaderImage()
}
func setHeaderImage() {
view.addSubview(headerImage)
headerImage.frame = CGRect(x: 0, y: 0, width: super.view.frame.width, height: 95)
headerImage.translatesAutoresizingMaskIntoConstraints = false
headerImage.mask?.contentMode = UIView.ContentMode.redraw
headerImage.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
headerImage.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
headerImage.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
headerImage.image = UIImage(named: "header")
view.sendSubviewToBack(headerImage)
}
}

As soon as you set headerImage.translatesAutoresizingMaskIntoConstraints = false, the frame is ignored. You need to set some constraint to establish the height of your UIImageView. Unfortunately, the image contents does not affect the height of the UIImageView.
Set either:
an absolute height constraint
an offset from the bottom of the superView
height relative to the width with a multiplier (an "aspect ratio constraint")
Based on my comment, you turned off headerImage.translatesAutoresizingMaskIntoConstraints = false and it worked.
This gives you extra constraints (your 3 plus the 4 that are generated from the frame), but luckily they aren't conflicting.
Instead, I would suggest you leave the translatesAutoresizingMaskIntoConstraints set to false and set a constraint for the height:
headerImage.heightAnchor.constraint(equalToConstant: 95).isActive = true

Related

Size (width/height) constraints on diff elements in UIStackView overriding each others' constraints

I am currently placing a UIImageView with a fixed width and height constraint of 20 and a UIView underneath that UIImageView. But the UIView has a width of 20, same as the UIImageView but when I add a width constraint of 10 to the UIView the UIImageView becomes 10x10... Below is my implementation: -
guard let superview = superview else { return }
verticalStack = UIStackView()
verticalStack?.translatesAutoresizingMaskIntoConstraints = false
verticalStack?.isLayoutMarginsRelativeArrangement = true
verticalStack?.axis = .vertical
verticalStack?.backgroundColor = UIColor.green
verticalStack?.spacing = 5.0
brightnessIcon = UIImageView(image: UIImage(named: "brightness.png"))
brightnessIcon?.contentMode = .scaleAspectFit
brightnessIcon?.widthAnchor.constraint(equalToConstant: 20.0).isActive = true
brightnessIcon?.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
verticalStack?.addArrangedSubview(brightnessIcon!)
brightnessIndicator = UIView()
brightnessIndicator?.backgroundColor = UIColor.red
brightnessIndicator?.widthAnchor.constraint(equalToConstant: 5.0).isActive = true
verticalStack?.addArrangedSubview(brightnessIndicator!)
Below is the image before I add the width constraint to the brightnessIndicator (UIView):-
After adding width constraint to the brightnessIndicator:-
You have to change your stackView's alignment. By default this is set to fill which tries to layout the arranged subviews to fill the opposite axis of your stackView (in your case they will be filled horizontally).
Try setting the alignment to leading, trailing, or centered (you have to check for yourself what fits your needs). As these alignment options don't force the arranged subviews to resize.
You need to set distribution and alignment of your stackView
verticalStack?.translatesAutoresizingMaskIntoConstraints = false
verticalStack?.isLayoutMarginsRelativeArrangement = true
verticalStack?.axis = .vertical
verticalStack?.alignment = .center
verticalStack?.distribution = .fillProportionally

Swift 5: centerXAnchor won't center my frame in the view

I'm stuck at the Consolidation IV challenge from hackingwithswift.com.
Right now I'm trying to create a hangman game. I thought to place placeholder labels based on the length of the answer word. These placeholder labels would be placed inside a frame, which then would be placed in the center of the main view.
Unfortunately, the leading edge of the frame is placed centered. In my opinion, this is not a problem of constraints, but rather a problem of me creating the frame wrong.
My current code is
import UIKit
class ViewController: UIViewController {
var answer: String!
override func viewDidLoad() {
super.viewDidLoad()
// MARK: - declare all the labels here
let letterView = UIView()
letterView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(letterView)
// MARK: - set constraints to all labels, buttons etc.
NSLayoutConstraint.activate([
letterView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
letterView.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor)
])
// MARK: - populate the letterView
// set the size of the placeholder
answer = "Atmosphäre"
let height = 60
let width = 25
// var width: Int
for placeholder in 0..<answer.count {
// create new label and give it a big font size
let placeholderLabel = UILabel()
placeholderLabel.text = "_"
placeholderLabel.font = UIFont.systemFont(ofSize: 36)
// calculate frame of this label
let frame = CGRect(x: placeholder * width, y: height, width: width, height: height)
placeholderLabel.frame = frame
// add label to the label view
letterView.addSubview(placeholderLabel)
}
}
}
The simulator screen looks just like this:
I already searched for answers on stackoverflow, but wasn't successful. I think I don't know what I'm exactly looking for.
The main problem, is that the letterView has no size, because no width or height constraints are applied to it.
To fix your code make the letterView big enough to contain the labels you've added as subviews by adding height and width constraints after the for loop:
for placeholder in 0..<answer.count {
...
}
NSLayoutConstraint.activate([
letterView.widthAnchor.constraint(equalToConstant: CGFloat(width * answer.count)),
letterView.heightAnchor.constraint(equalToConstant: CGFloat(height))
])
I'm not sure if you've covered this in your course yet, but a better way to go about this (which would take much less code), is to use a UIStackView as your letterView instead.
An extra thing to consider:
If you give the letterView a background color, you'll see that the labels are actually aligned outside of its bounds:
That's because you're setting each label's y position to be height, when it should probably be zero:
let frame = CGRect(x: placeholder * width, y: 0, width: width, height: height)
Correcting this places the labels within the bounds of the letterView:

How to center a view on the x axis using autolayout in Swift on MacOS?

Hi I'm just wanting to have a childView centered on the x axis of a NSViewController view using auto layout. I'm doing the following:
override func viewWillAppear()
{
super.viewWillAppear()
let childView = PagesView.init(frame: CGRect(x: 0, y: 0, width:100, height: 100))
childView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(childView)
childView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
}
The childView doesn't even appear on the screen. When I remove the last line, then it gets positioned at 0,0. So something about that last line is causing it go haywire and I'm not sure why? I've used this exact same logic on iOS and it has worked fine.
All views should have constraints to define its location and size.
// This would create dimension constraints if your class cannot already infer them
childView.widthAnchor.constraint(equalToConstant: 100).isActive = true
childView.heightAnchor.constraint(equalToConstant: 100).isActive = true
You also need to specify a vertical anchor constraint so the view has enough information to be displayed.
childView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true

Creating UILabel programmatically with constraints?

What I want to do is create a new UILabel programmatically every time a certain action occurs in my code. I know the x, y, and height that I want to give the label, but I don't want to give it a set width. I want to constrain the sides so that the UILabel width is equal to the width of the screen, and so that the label width will change if the orientation is flipped.
I have considered using:
CGRect(x:, y:, width:, height:)
However, I would have to give it a set width if I use this, so I don't think it will work.
I also tried using:
CGPoint(x:, y:)
Then setting leading, trailing and height anchors, however, this doesn't seem to work either as even though it does compile, I get an error when I try creating a new UILabel.
I'm kind of new to programming in Swift so I'm not sure if there is an obvious fix to this.
Like you said, we already have x, y and height available for the label, i.e.
let x: CGFloat = 0
let y: CGFloat = 0
let height: CGFloat = 50
Let's create a label using the above details. Also set the width of the label as UIScreen.main.bounds.width as per your requirement.
let label = UILabel(frame: CGRect(x: x, y: y, width: UIScreen.main.bounds.width, height: height))
label.text = "This is a sample text"
Don't forget to set label's translatesAutoresizingMaskIntoConstraints as false and add it to whatever subview you want.
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
Add the relevant constraints of label with its superview - top, bottom, leading, height. You can definitely add bottom constraint if required. That totally depends upon your UI.
I'm adding the label to the top of the viewController's view.
NSLayoutConstraint.activate([
label.heightAnchor.constraint(equalToConstant: height),
view.topAnchor.constraint(equalTo: label.topAnchor),
view.leadingAnchor.constraint(equalTo: label.leadingAnchor),
view.trailingAnchor.constraint(equalTo: label.trailingAnchor)
])
You can use following code to create UILabel programmatically.
private let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.text = "Hello World"
return label
}()
Then inside your viewDidLoad()
addSubview(label)
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: label.topAnchor),
bottomAnchor.constraint(equalTo: label.bottomAnchor),
leadingAnchor.constraint(equalTo: label.leadingAnchor),
trailingAnchor.constraint(equalTo: label.trailingAnchor)
])

Swift: Retain floating point decimal in autolayout

this is a follow on question from my post here. I am attempting to fix my heightConstraint of imageView dynamically depending on the size of my image, and then add a drawing layer over it. However, I noticed that the imageView size loses precision due to the division of floating point and this resulted in a blurred image when I start to draw on my image, ie the placement of the bitmap is not exactly at the position overlaying my original image.
My code as such, with the print statements to observe the decimal places.
func setupViews() {
view.backgroundColor = .black
view.addSubview(canvasImageView)
canvasImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
canvasImageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
canvasImageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
let aspectRatio = getImageAspectRatio(image: image)
let screenWidth = UIScreen.main.bounds.width
let height = CGFloat(1.0) / CGFloat(aspectRatio) * CGFloat(screenWidth)
canvasImageView.heightAnchor.constraint(equalToConstant: height).isActive = true
print("ImageSize", image.size.height)
print("Aspect Ratio image at start:", aspectRatio)
print("Calculated height:", height)
DispatchQueue.main.async {
print("Aspect Ratio imageView at start:", self.getAspectRatio(frame: self.canvasImageView.frame))
print("ImageViewSize", self.canvasImageView.frame.height)
}
}
My print statements are here:
ImageSize 3000.0 2002.0
Aspect Ratio image at start: 1.4985014985015
Calculated height: 213.546666666667
Aspect Ratio imageView at start: 1.49532710280374
ImageViewSize 320.0 214.0
As you can see, my attempt to do the heightAnchor.constraint(equalToConstant: height) actually loses the precision and resulting in a lost of resolution. Is there a way to eliminate this? An image of the outcome is here.