Flexible image views - ios

I'm working on a view that contains three UIImageViews. The view and the child imageviews are created in the storyboard. The images for these imageviews are loaded from a server.
When the server returns images for all three images, it should look like this:
When there is no image available for the third imageview, the layout should dynamically change like this (with image scaling into the imageview etc, I know that part):
I am already working quite a while with the Interface Builder, but have no idea how to achieve this..
Anyone can help me out figuring this, preferred using autosizing in IB?

This kind of possible using autolayout, but you still have to modify one constraint programatically, based on whether you are displaying one or two UIImageViews.
Here's how you do it, with autolayout:
Create The Container View
Create a UIView as a container.
Move the left edge of the container view to the left margin.
Move the right edge of the container view to the right margin.
Pin the container view's height.
Delete the constraint for the bottom space to superview.
Create The UIImageViews
Create the three UIImageViews inside the container view.
Size and align the UIImage views as you want them to display.
Pin the horizontal spacing between the first and second image view.
Pin the horizontal spacing between the second and third image view.
Pin the leading space on the first image view to the superview.
Pin the trailing space on the third image view to the superview.
Select all three image views and pin the widths equally.
Delete the align center x constraint on the second image view.
Select the trailing space to image view constraint on the second image view and set the priority to 900 (important).
Create the Magic Extra Constraint
Pin the trailing space to superview on the second image view.
Select the new constraint and set the priority to 850.
Set the constant value for the constraint to 0.
The container UIView is simply to maintain the margins and height for the containing views.
The second image view should now have two trailing constraints, both appear as dotted lines. Rotating the screen should have the container view stretching to fit, and the three UIImageViews also stretch to fit.
Currently, the constraint between the second and third image views is higher priority than the "magic" constraint between the second image view and the container view, causing the second image view's right edge to be spaced away from the third image view's left edge. To adjust the layout for just two images, the "magic" constraint has to be higher priority than the other one, causing the second image view's right edge to align with the superview's right edge.
Simply create an IBOutlet for the "magic" constraint (trailing space to superview on the second image view) and raise and lower the priority as needed.
if (imageCount == 2) {
// Second image view should favor right edge of superview
self.myConstraint.priority = 950;
} else if (imageCount == 3) {
// Second image view should favor left edge of third image view
self.myConstraint.priority = 850;
}
You may also wish to hide the third image view when necessary.

My option would be that when third image is null set the isHidden property of third imageView to YES. Then specify the bounds of the other two imageviews accordingly(which, you said, you know how to do).

Check if imageView_3 have no image and then adjust size of rest two imageViews such as:
if (imageView_3.image == nil) {
CGRect frame = imageView_1.frame;
frame.size.width += imageView_3.frame.size.width / 2;
imageView_1.frame = frame;
frame = imageView_2.frame;
frame.size.width += imageView_3.frame.size.width / 2;
frame.origin.x += imageView_1.frame.origin.x + imageView_1.frame.size.width + 10/*the gap*/;
imageView_2.frame = frame;
}

In my opinion you can check the image count coming from server that is whether there is three image or two image by counting the array count. Then you can set different frame to UIImageView based on the array count.
if([imageArray count] == 3) {
//set the frame of the images so that it can fit the screen.
}
else if([imageArray count] == 2) {
// set the frame of the UIImageView so that it can fit the screen and hide the third UIImageView
[thirdImageView setHidden:YES];
}
Like this you can set the frame for UIImageView

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.

Setting top constraint on UIImageView causes UILabel to be fixed height

I'm having this weird issue with my constraints which causes the UILabel (Caption Label) to be a fixed height instead of dynamically changing height depending on the text.
I have a view (Vertical View) with a top constraint on the label above it. The Vertical View contains a view (called View) which I'm using as a divider that is centered from top to bottom with a width of 1. On the left of the divider is a UIImageView (Left Image View) with constraints leading, top, bottom equal to superview and trailing equal to View. I want to do the exact same thing to the UIImageView on the right of the divider but here is where my issue comes up.
If I use a fixed height as seen below, the UILabel above Vertical View dynamically changes its height like I want but this is obviously not how I want the UIImageView on the right to appear. I want it to be similar to the UIImageView on the left of the divider with equal height and width.
If I set the top constraint of the UIImageView on the right to the superview Vertical View, similar to the UIImageView on the left of the divider, the UILabel above Vertical View doesn't dynamically change height anymore. The UILabel now has a fixed height which I believe comes from the fact that UILabel has a height of >= 14.
How can I properly set the constraints so that I can have both UIImageViews next to each other with equal and height contained within the Vertical View and still have the UILabel above Vertical View dynamically change height depending on the text that I set the UILabel to?
On the RightImageView, you first need to get rid of the "Height = 50" constraint. This is what is causing it to be small.
Next, if that alone doesn't fix you, can you try setting the following constraints instead of using the superview for the constrains (instead make it mirror the LeftImageView):
Left: Leading spacing to divider view
Top: Align top edges to LeftImageView
Right: Horizontal space to superview (your vertical container view)
Bottom: Align bottom edges to LeftImageView
This should allow the views to remain the same height and width (assuming your distances between left/right edge of vertical container view are the same, and the distances between divider are the same).
Now, ensure the size constraint for width of the divider is set to 1 and not >= 1. Also, ensure the vertical container view has a Compression lower than the Label.
One final note--your screenshot shows the result that IB is showing you (with the dotted yellow box) on the LeftImageView. One you update your constraints correctly, this yellow box should go away.
Regarding the UILabel - if you want this to grow dynamically, you need to do the following:
myUILabel.numberOfLines = 0;
myUILabel.text = #"Enter large amount of text here";
[myUILabel sizeToFit];

how to make position of view dynamic using constrants in ios?

I am developing an app in which i am showing a image view.There are some red dots on image which are UIButton.When user tap on button then image should zoom.For that i have added a scroll view with constraints as below
Inside scroll view i have imageview
Now i when my screen size changes then image size also changes.I have added a UIButton as red dot.I have given it's constraints horizontal & vertical line.
Now when device size change then button position on image also change.
What i want the button position should also be same on all device even if the image size grows.Please tell me how can i do that.
Add the button on top of the UIImage View
then add constraints by dragging from the button to the image.
first put two constraints for leading space from UIImageView and top
space from UIImageView to be a constant or standard depending on ur
needs.
It would look something like this as ur code..
button.top = image.top
button.leading = image.leading
adding these two constraints will do ur work.
If u want to fix the size of the button u need to add hieght and
width constraints.
ill attach an image on how it looks like
here the weather icon is the image and i m setting constraints on the button
(remove the constraints for trailing and bottom)
To place the button according to the image
set the image to a known size say 100 x 100
and then place the button at the required place.
now check the distance from the top of the image to the button.
ex. if it is say 12 then ur ratio will be 12:100
now set a constant (button.top) to 12.
so now if the height changes to say 200 multiply it by the ratio
i.e image.height* 12/100
in the given example constant = 200(new image hieght) *(12/100)(ratio) = 24
so the new distance of the button.top = 24
Like a view, you can drag and drop the constraints into the viewController.m file:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *myLayoutConstraint;
where you can manipulate programmatically:
[UIView animateWithDuration:1.0 animations:^{
self.myLayoutConstraint.constant = 10;
} completion:^(BOOL finished) {
//handle completion
}];

UIScrollView autolayout - vertical scroll only

UIScrollView is a little special in the way it interprets auto layout constraints, as geometry of the subviews fills its contentSize as opposed to frame/bounds. Apple has a nice write up on the matter here: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
The problem I'm facing is when I try in place multiline labels into the scrollView.
An example constrain looks like this:
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[image(==imageWidth)]-15-[captionLabel]|", options: .AlignAllCenterY, metrics: metrics, views: views))
I'm not showing additional subviews that are handled by other scrollView.addConstraints() calls which take up more vertical space.
Ideally, I want both elements to fit horizontally within the screen, and have the captionLabel break into multiple lines as necessary. Instead, the whole text is rendered into a single line and the scrollView sets its contentSize to accommodate the larger width.
What options are there to solve with auto layout?
Edit: Not too happy about nesting more contentViews, so I ended up setting the desired width of the label in a separate constraint, and updating it in viewWillTransitionToSize. In my case, a whole list of similar "rows" of labels in used, and only the first one has width set through a calculated constraint, the rest are set to be equal to the first.
First your view hierarchy should looks like this:
the most important is that you have only one child in your scroll view it will act as a content view.
The constraints :
ScrollView = 4 constraints from edges to the main view with 0 spacing
ContentView = 4 constraints from edges to the ScrollView with 0 spacing
And to avoid horizontal scrolling
add a constraint of equal width between ContentView and Main view (not ScrollView)
now add content to your content view. Use Auto Layout, like you normally would. Don't forget to set 0 as number of lines for your label add the top, right, left bottom constraints of the label.
The result should be like mine :
More information: Using UIScrollView with Auto Layout in iOS
I don't know if this can help you (i hope understand good the question), but if you only want scroll Vertical, you can add this in your implementation:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Vertical Only ** If you have several scrolling objects detect by Tag
if (scrollView.tag == 100) {
CGPoint offset = scrollView.contentOffset;
offset.x = 0;
scrollView.contentOffset = offset;
}
}
If you want a storyboard solution, you can set the constraint "Equal Widths" twice: between your UIScrollView and its Superview, and between your UILabel and your UIScrollView

How to make uiscrollview only vertical scrolling for ios?

I'm trying to make layout inside scrollview using this one tutorial link
And get the following result link
It will be appreciated for any advices or tutorial links. It needs only vertical scrolling
I am sure there must be other ways to do this but a quick fix is :
1.) Create a width constraint on ContentView in Storyborad.
2.) IBOutlet that widthContraint and set its value to the view frame width in viewDidLoad.
Suppose the name of the constraint outlet is contentViewWidthContraint.
contentViewWidthContraint.constant = self.view.bounds.size.width;
Another alternative to do so from Storyboard, is to fix the Contentview width to the view's width from the storyboard or to the Scrollview, if Scrollview already has a Equal width contraint with superview . Add the "Equal Width" contraint from Contentview to either self.view or to Scrollview (if scrollview, already has the width contraint)
Have you set up the "ContentView" width to match with the scroll view width? I had the same problem and I fixed with "Equal Widths".
"Equal Widths" will tell to your "ContentView" to use the same width of the "Scroll View", which should be fitting the screen if you have set up the constrain properly.
You can do this easily on the storyboard.
Drag and drop, with right click (important!!!), from "ContentView" to "ScrollView"
Release the click, you will be prompted with a menu, select "Equal Widths".
This should fix your problem using the scrollview with AutoLayout from Storyboard editor.
You can find a full tutorial how to use ScrollView with Autolayout and Storyboard here.
I hope this is useful for you :)
In the Storyboard set the width of the elements contained in your UIScrollView equal to the width of this UIScrollView (by selecting all elements and the UIScrollView holding in the panel on the left of your Storyboard and then setting the 'Equal Widths' constraint under 'Pin' on the bottom of your Storyboard). Just pinning the right sides of the elements to that of the UIScrollView won't work as it will adjust the size of its "display view" to the width of the largest element and if this is smaller than the width of the UIScrollView all elements will just appear aligned to its left side.
There is also another possibility that offers a very good result.
You can mark a checkbox:
O programmatically:
scrollView.alwaysBounceVertical = true
Try to set it's width to 0 & height equal to content size like this:
self.scrollView.contentSize = CGSizeMake(0, self.scrollView.contentSize.height);
This will work as you want. Try it & tell if still facing any issue.
For disabling the horizontal scroll, you can set the content size in the -(void)scrollViewDidScroll method.
[self.scrollView setContentOffset: CGPointMake(0, self.scrollView.contentOffset.y)];
self.scrollView.directionalLockEnabled = YES;
This is because scroll view have no idea where your content should end.
But when at least one item inside your scroll view has its "trailing space" constraint attached to a view outside the scroll view (usually a view the scroll view is sitting in or some other view of a higher level, which "knows" its width) - the scroll view will automatically get an idea about your wanted width and won't scroll horizontally (unless that trailing constraint implies having your content outside the screen).
Better if all items inside scroll view have their "trailing space" constraints connected either to each other or to a view outside the scroll view. But not the scroll view itself.
No additional code or extra constraints needed for this to work.
Too set UIScrollView constraints as like below code so it will occupied whole screen.Not exceed the screen size.
Leading Space = 0 from mainView
Top Space = 0 from mainView
Bottom Space = 0 from mainView
Trailing Space = 0 from mainView
You need to set the width of UIScrollView equal to or less than the width of your Parent View. Two ways to do it:
1) You can do this in Storyboard via layout constraints
2) You can do this programatically:
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.scrollView.contentSize.height);

Resources