Center Align UIImageview and Large UILabel AutoLayout - ios

I have a UIImageView and a UILabel. Label has greater height than image, so I want to center align Y position for both.
Here is original case:
When I apply Center Y from image to label, label content is truncated.
Please can someone guide me for correct constraints... Thanks!

I am using the setup above in my storyboard. A UIImageView on the left and a UITextView on the right; setup in a UIViewController. I setup my constraints like this:
UIImageView
Width Equals: // Whatever you want
Height Equals: // Whatever you want
Align Center Y: Text View
Leading Space to Superview: 16
Trailing Space to Text View: 8
UITextView
Align Center Y: Image View
Trailing Space to Superview: 16
Leading Space to Image View: 8
Top Space to Top Layout Guide: 16
Height Equals: // Whatever you want; this will change
The center Y constraint will keep the UIImageView centered with the UITextView. The top space constraint on the UITextView is what will set the vertical positioning for the views. The height constraint on the UITextView is where the dynamic height portion comes in. Make an outlet to your view controller for the UITextView and the text view height constraint. Also set yourself as the text view's delegate. This will allow us to change everything dynamically as text in the text view is changed.
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var textViewHeight: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self
}
func textViewDidChange(textView: UITextView) {
// Ask the text view how much size it needs to fit its content if it has to
// fit in its current width but can grow vertically as much as it needs
let sizeToFitIn = CGSizeMake(textView.bounds.size.width, CGFloat(MAXFLOAT))
let newSize = self.textView.sizeThatFits(sizeToFitIn)
self.textViewHeight.constant = newSize.height
}
}

I don't use a center constraint if I want to center something because it doesn't work for me always either, as you said. What i do is i add a constraint for leading space and trailing space for X centering and top space and bottom space for Y centering.
That would center your label with 30px space on the left and right side. If you want to use the entire space just make the constant to 0. (For Y just change the other two --> Note to activate the constraint you have to click on the red line)

Related

Prevent vertical UIStackView from stretching a subview?

I have a UITableViewCell with a vertical UIStackView that currently has .alignment = fill and distribution = fill. Inside the stack view, I have a UILabel and a UIImageView.
The label should be left aligned and stretch across the width of the stackview (and it does). The image view has a content mode of aspect fit and should be centered in the middle of the stackview. I thought this was working as I wanted it to until I set a background color on the UIImageView and then realized that while the image itself appears correct (scaled and centered) the view itself stretches across the full width of the stackview (just like the label does).
Is it possible to have the label stretch the width of the stack view but not the image view? Ideally, I'd like the image view to just be the size of the scaled image and not stretch all the way to the edges of the stack view.
Make these changes:
Set your stack view's alignment to .center instead of .fill.
Constrain the label's width to equal the stack view's width.
In code, when you set the image view's image, also create an aspect-ratio constraint on the image view, like this:
class MyCell: UITableViewCell {
#IBOutlet private var myImageView: UIImageView!
private var imageViewAspectConstraint: NSLayoutConstraint?
func setImage(_ image: UIImage) {
myImageView.image = image
imageViewAspectConstraint?.isActive = false
imageViewAspectConstraint = myImageView.widthAnchor.constraint(
equalTo: myImageView.heightAnchor,
multiplier: image.size.width / image.size.height)
imageViewAspectConstraint!.isActive = true
}
}
Note that, since cells can be reused, you also have to remove a prior aspect-ratio constraint if there is one. The code I posted uses imageViewAspectConstraint to save the constraint and remove it when the image changes.
One approach:
calculate appropriate aspect-fit frame for your image
When the user taps on the image view, evaluate the tap position and only take action if the tap is within the aspect-fit frame.
Another approach:
calculate appropriate aspect-fit frame for your image
embed the UIImageView horizontally centered inside a UILayoutGuide
add the UILayoutGuide as the stack view's arranged subview
This will keep your label stretched across the width of the stack view (the cell) and center your image view, allowing you to detect the taps.

View at the bottom in a UIScrollView, with AutoLayout

I'm setting up content in a scroll view with autolayout. The objects in the scrollview are pinned top-to-bottom to the previous one, so that they are under one another. I have a footer view that is added at the end, below these objects.
Here's the catch: when there's few content, the contentView will be smaller than the screen height, so the footer view will appear somewhere in the middle of the screen (which is the normal behavior). But I'd like to prevent that, and make the view stay somewhere at the bottom.
In other words, I would like to setup a double constraint like:
Put this view below all the objects in the scrollview
AND
keep this view at a distance of max [some number] of the bottom of the screen
In a way that both constraints are always satisfied:
If the height of the content is bigger than the screen, then the view appears at the bottom, after scrolling down
If the height is smaller, then the view is "pinned" to the bottom of the screen, leaving a space relatively big between the bottom of the content and the top of this view
How can I achieve that with AutoLayout?
Fairly easy to do with Auto-Layout only... no code required.
The key is to use a "content view" to hold the elements, and a greater-than-or-equal constraint between your "bottom" element and your "footer" view.
In this image, yellow is the main view, green is the scroll view, blue is the content view, the labels are gray and the footer view is pink.
Start with a fresh view controller
add a scroll view, normal constraints (I used 20 all the way around, so we can see the frame)
add a UIView to the scrollView - this will be our "content view"
constrain contentView Top/Bottom/Leading/Trailing all equal to 0 to the scrollView
constrain both the Width and Height of the contentView equal to the scrollView
add your elements - here I used 3 labels
constrain the labels as usual... I used:
LabelA - Top/Leading/Trailing all at 20, vertical spacing to LabelB of 60
LabelB - Leading/Trailing at 20, vertical spacing to LabelC of 60
LabelC - Leading/Trailing at 20
LabelC is also set to Number of Lines: 0 so it will expand with multiple lines of text
Add a UIView as a "footer view" (I stuck a label in it)
constrain the footerView Leading/Trailing/Bottom all at 20 (so we can see the frame)
either set a Height constraint on footerView, or use its content to constrain its height
add a Vertical Spacing constraint from LabelC to footerView, and set it to >= 40
last step, change the Height constraint of contentView to Priority: 250
Now, as you expand/contract the height of LabelC, the footerView will keep at least 40-pts of vertical space. When LabelC gets big enough to "push" footerView below the bottom, scrollView will become scrollable.
Results:
you need to check ContentSize of scrollView and modify FooterView Top Constraint with the required Value
My class code
import UIKit
class scrollViewDrag: UIViewController
{
/// ScrollView Outlet
#IBOutlet weak var mainScrollView: UIScrollView!
/// Footer View top spacing constraint
#IBOutlet weak var footerViewTopConstraint: NSLayoutConstraint!
/// Used for ScrollView Height
var screenHeight = CGFloat()
/// Did Load
override func viewDidLoad() {
super.viewDidLoad()
}
/// Function used to check for height
func checkForHeight(){
/// Get scrollView Height
screenHeight = mainScrollView.frame.size.height
/// Check contentSize Height ?
if mainScrollView.contentSize.height >= screenHeight {
/// When ScrollView is having height greater than your scrollView Height
/// Footer will scroll along other Views
}
else{
/// Issue Case
let spacingValue = screenHeight-mainScrollView.contentSize.height
footerViewTopConstraint.constant = spacingValue
}
}
/// Call the height function in DidAppear
override func viewDidAppear(_ animated: Bool) {
checkForHeight()
}
}
Storyboard
I had used Four View with Equal Heights And at last a footerView is attached as Fourth View
FooterView Top Constraint
Top constraint used as footerViewTopConstraint
Output
Case 1 - Size is greater than scrollView Height
Case 2 - Expected Output

what is wrong with constraints in my UIImageView and why I cannot stretch the image on my UIViewController?

This is my current situation:
I have a UIViewController with UIImageView on it.
The latter has the following constraints: trailing to superview, leading to superview, top space to top layout guide, width equals 320, height equals 820.
Height and width are connected as outlets to my class and I'm setting them in viewDidLoad:
#IBOutlet weak var imageHeightconstraint: NSLayoutConstraint!
#IBOutlet weak var imageWidthConstraint: NSLayoutConstraint!
#IBOutlet weak var backgroundImage: UIImageView!
override viewDidLoad(){
backgroundImage.af_setImageWithURL(NSURL(string: photoURL)!)
imageHeightconstraint.constant = backgroundImage.image!.size.height
imageWidthConstraint.constant = self.view.frame.size.width
}
Now, my image is this:
it is 480x640px photo. I tried in my storyboard to set the mode to center, scale to fill, aspect fill, aspect fit and top.
For example, this is center:
aspect fit:
aspect fill:
but I don't know how to stretch this image from left to right and keep the aspect ratio, so the whole photo is visible and its left and right edges stick to the left and right edge of the screen.
I thought those two constraints could fix it:
imageHeightconstraint.constant = backgroundImage.image!.size.height
imageWidthConstraint.constant = self.view.frame.size.width
but it does not work. Maybe the problem is that the resolution of the photo is low (480x640), so the height is only that small? Can you give me any hint how could I fix it?
To have the image stick to both sides of screen and maintain the aspect ratio, set these 4 constraints:
Leading Edge of ImageView to Leading Edge of SuperView.
Trailing Edge of ImageView to Trailing Edge of SuperView.
Top Edge of ImageView to Top Layout Guide Bottom.
Set an aspect ratio constraint: ImageView Width equal to ImageView Height with multiplier 480:640. To set this, control-drag diagonally in the ImageView and select Aspect Ratio from the pop-up. Then change the multiplier to 480:640.
Set the content mode for the image to Scale to fill.
You may not be calling setNeedsLayout() and then layoutIfNeeded().
In order for the UI to update, you need to tell the view to update, not just change the constraints.
Either way, if you want the imageView to consistently fill the screen and you shouldn't have a height/width on the ImageView, only hugging the sides. Also make sure those constraints are ignoring margins.
If you want the image to fill the imageView, set the resizing mode to aspectFill, and If you're looking for it to resize until the left/right or top/bottom reaches the edge of the view, aspectFit is what you need.

ScrollView not resizing properly

I have a ScrollView embedded in my ViewController. Inside the ScrollView I have embedded a content view (UIView) which has a UIImage set to match the left, top and right of the ScrollView with a dynamic height that changes depending on the aspect ratio of an image that the user can load after the ViewController loads. There are three buttons all horizontally aligned in the content view and spaced evenly apart from each other.
When the user loads in a photo that is too big for the screen it should just resize the ScrollView and the content view appropriately to allow scrolling to see the buttons but instead it just bunches up the buttons at the bottom of the screen.
Here is how the buttons should look:
Here is what happens when the photo is too big:
Here are the constraints of the ScrollView:
Here is my resizing code:
let img : UIImage = info[UIImagePickerControllerOriginalImage] as! UIImage
let screenSize: CGRect = UIScreen.mainScreen().bounds
let multiplyNum = screenSize.width / img.size.width
imageViewHeightConstraint.constant = (multiplyNum*img.size.height)
imageView.image = img
Even when I try and change the ScrollViews height programatically to a very large number it still won't get any larger than the ViewController (no scrolling).
Constraints of ContentView:
Constraint of ImageView:
Constraints of first 2 buttons:
Constraints of last button:
Make sure to set all the constraints that completely define the vertical layout of all elements (top constraint for image, vertical space between elements and bottom constraint of last element), and try changing the priority of the content hugging or compression resistance of the elements.
Edit:
You can achieve that behaviour with this view hierarchy:
- UIView
- UIScrollView
- UIImage
- UIButton
- UIButton
- UIButton
There is no necessity to add a container view if you set the constraints like this:
scrollView:
Leading, trailing, top and bottom constraints to view (all 0)
inner views (horizontal):
Leading constraint from imageView to scrollView
Trailing constraint from imageView to scrollView
Equal width from imageView to view
inner views (vertical):
Top constraint from imageView to scrollView
Height constraint of imageView (this constraint constant will change depending on the size of the image)
Horizontal space from imageView to button1
Horizontal space from button1 to button2
Horizontal space from button2 to button3
Bottom constraint from button3 to scrollView
There is no necessity to change the priority of the content hugging or compression resistance of the elements.
I already check that it works in a project.

Moving UILabel dynamically based on UITextView contents - Using Storyboard

Using storyboard, I am trying to put a UILabel below UITextView. Content of UITextView can grow based on user input. I want my UILabel to reposition its Y value based on UITextView contents. I've tried fixing vertical spacing between UITextView and UILabel but that is not helping.
What else I shall be doing to crack this?
You should use constraints explicitly. The label in particular must have the constraint "top space to Textview" set to N points. Then set an outlet in your view controller from the constraint "top space to Textview" and change that constraint to resize uilabel Y position.
#IBOutlet weak var labelYConstraint: NSLayoutConstraint!
func changeLabelPosition(sender: AnyObject) {
labelYConstraint.constant = newY
}
Let me know if you need more details

Resources