Can't get a UIImageView fill the parent UIView with autolayout - objective-c

I'm trying to fill a UIView with a UIImageView using auto layout. I've tried several thing, but the most obvious, equalSize with parent view only works when I set the correct size in "Simulated metric". If I let Freeform (the point of autolayout isn't it ?) I get messed up.
I'm testing on an iPhone 4S and a 6 plus.
Thanks for any leading tracks.
EDIT FOR #mittens
I've seen your edit. I still can get it working. As you see I have 4 margin constraints, the same as in your code (I did not need the rest because I only use the main UIView).
Anyway, when I change the size on xcode, the layout is perfect, when I send it to my 4S, I only get Top and left margins.

I'd double check the constraints on the UIView you're trying to fill with the UIImageView to make sure it is filling up its parent view as it should be when the storyboard/xib is set to freeform. Also if you're adding/creating the views programmatically double check that the views 'translatesAutoresizingMaskIntoConstraintsis set toNO`. That always trips me up.
I made a super quick view and added some constraints to both a view inside the View Controllers view and an Image view to show one way to do it -- hopefully it helps at least a little
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.backdropView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
self.backdropView.backgroundColor = [UIColor colorWithRed:0 green:1 blue:0 alpha:0.5];
self.backdropView.translatesAutoresizingMaskIntoConstraints = NO; // Make sure this is set to NO if the view is being added programmatically
[self.view addSubview:self.backdropView]; // Always add the view into the hierarchy _before_ constraints are added (again, if creating & adding programmatically)
NSLayoutConstraint *backdropViewWidth = [NSLayoutConstraint constraintWithItem:self.backdropView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.backdropView.superview attribute:NSLayoutAttributeWidth multiplier:0.5 constant:0];
NSLayoutConstraint *backdropViewHeight = [NSLayoutConstraint constraintWithItem:self.backdropView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.backdropView.superview attribute:NSLayoutAttributeHeight multiplier:0.5 constant:0];
NSLayoutConstraint *backdropViewCenterX = [NSLayoutConstraint constraintWithItem:self.backdropView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.backdropView.superview attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
NSLayoutConstraint *backdropViewCenterY = [NSLayoutConstraint constraintWithItem:self.backdropView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.backdropView.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
[self.backdropView.superview addConstraints:#[backdropViewWidth, backdropViewHeight, backdropViewCenterY, backdropViewCenterX]];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
self.imageView.backgroundColor = [UIColor colorWithRed:1 green:0 blue:1 alpha:0.5];
[self.backdropView addSubview:self.imageView];
NSLayoutConstraint *imageViewTop = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.imageView.superview attribute:NSLayoutAttributeTop multiplier:1 constant:8];
NSLayoutConstraint *imageViewLeft = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.imageView.superview attribute:NSLayoutAttributeLeft multiplier:1 constant:8];
NSLayoutConstraint *imageViewRight = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.imageView.superview attribute:NSLayoutAttributeRight multiplier:1 constant:-8];
NSLayoutConstraint *imageViewBottom = [NSLayoutConstraint constraintWithItem:self.imageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.imageView.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:-8];
[self.imageView.superview addConstraints:#[imageViewTop, imageViewLeft, imageViewRight, imageViewBottom]];
[self.view layoutIfNeeded];
}
That produces and
Again, hopefully this helps.
EDIT: how to add them in storyboard
Note on photo 3, I select the view I want to constrain and then shift select the view I want to constraint it to. So I selected the inside UIView (because it width/height will be constrained to the parent view) then shift selected its parent view (the view it is nested inside of) to enable those options of Equal Width & Equal Height
Add constraints to UIImageView nested inside the UIView
Center UIView inside its parent view
Add width/height constraints equal to parent -- Note: I select the view I want to constrain and then shift select the view I want to constraint it to. So I selected the inside UIView (because it width/height will be constrained to the parent view) then shift selected its parent view (the view it is nested inside of) to enable those options of Equal Width & Equal Height
Change multiplier in constraint to be whatever you want, 0.5 in this case
Celebrate

i'm not sure but try this in your imageview.
[imgView clipToBounds:YES];
I hope it's work for you

Related

NSLayoutconstraint created programmatically fails

I am on Xcode 8.3.3, OSX not iOS.
I have a customView I use as container to replace other views inside it.
The dark blue box on the right represents the container:
The constraints of the container are set in IB as follows where "scrollView" is the tables scroll view left of it:
This enables vertical scaling (scrollView is allowed to scale vertically) which is the desired behaviour:
Now I add a subview to the container with this code:
NSStoryboard *storyBoard = [NSStoryboard storyboardWithName:#"Main" bundle:nil];
_previewViewController = [storyBoard instantiateControllerWithIdentifier:#"showSHPreviewViewController"];
CGRect newFrame = CGRectMake(0, 0,CGRectGetWidth(_previewViewController.view.bounds),CGRectGetHeight(_previewViewController.view.bounds));
_previewViewController.view.frame = newFrame;
[self.previewContainer addSubview:_previewViewController.view];
This works as expected:
My Problem is, I want the subview to be stretched so it fits the whole height of its container. Therefore I have to set NSLayoutConstraints programmatically. But i don't get the logic.... Whatever I try leads to a behaviour where neither the scroll view nor the container can scale vertically at all.
This was my last try before I wrote this post:
[self.previewContainer addConstraint:[NSLayoutConstraint
constraintWithItem:_previewViewController.view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.previewContainer
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0]];
[self.previewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_previewViewController.view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.previewContainer
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0]];
[self.previewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_previewViewController.view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.previewContainer
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0]];
I can confirm that the content of the added subview is setup correctly and resizeable (no "height-lock").

Objective C Constraints in Classes

I'm currently attempting to add a UIIMageView on top of a class.
Without constraints, I can add an object such as an UIImageView without any hindrance: [self addSubview:myImage];.
However, when I add [myImage setTranslatesAutoresizingMaskIntoConstraints: NO]; along with the needed constraints, the image is added to the UIViewController versus the class.
(Right now, the sizeX/Y is a separate algorithm to set the image width and height and won't be needed assuming constraints will work)
1) Why is that?
2) How can add a UIImageView with constraints?
Here's the code:
UIImageView *myImage = [[UIImageView alloc] init];
//AutoResize...
myImage.frame = CGRectMake(0, 0, sizeX, sizeY);
myImage.center = CGPointMake(placementX, placementY);
myImage.image = [UIImage imageNamed:#"customImage"];
[self addSubview:myImage];
// Width constraint
[self addConstraint:[NSLayoutConstraint constraintWithItem:myImage
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// Height constraint
[self addConstraint:[NSLayoutConstraint constraintWithItem:myImage
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
Thanks!
I'm currently attempting to add a UIMageView on top of a class...
...the image is added to the view controller versus the class.
Neither of these statements make sense to me, but...
Looking at your constraint code, you set constraints for the width and height of the image view, but that is not enough to unambiguously position the image view. You need to add 2 constraints: for the vertical and horizontal position of the image view. For example if you want the image view centered in its superview:
[self addConstraint:[NSLayoutConstraint constraintWithItem:myImage
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:myImage
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0]];

How to resize subview subview based on autolayout?

I am having a NSView let call this Detail View in my main view controller in which all the auto layout constraints will be added in dependent with the main view. I will be adding a subview lets call this sub detail to this Detail View based on user input. This sub detail view contains lot of view for which constraints are added in accordance with the sub detail view. While adding the sub detail to Detail View as sub view I am setting the frame by getting the frame of Detail View and setting x = 0 and y = 0;. My problem is that when a user maximise the window and user select and I am adding the sub detail view to Detail View the size of sub detail subview remains the same but sub detail view get auto resized as per required. Could any one guide me what i am doing wrong. Below i am sharing you the code where in which i am adding the subview and output view in the link
I don't want the blank blue space.
NSRect f = detailView.frame;
f.origin.x = 0;
f.origin.y = 0;
ConfigLogin *subDetail = [[ConfigLogin alloc] initWithFrame:f];
[subDetail setWantsLayer:YES];
[subDetail setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
[subDetail setTranslatesAutoresizingMaskIntoConstraints:YES];
subDetail.layer.backgroundColor = [NSColor blueColor].CGColor;
subDetail.frame = f;
[detailView addSubview:detailView];
I could find the answer as while adding the subview we are not adding the constraints to make the view fit to the super view.
+(void) fitSubViewToSuperView:(NSView *) subview superView:(NSView *) superView
{
NSLayoutConstraint *width = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeWidth
relatedBy:0
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
NSLayoutConstraint *height = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeHeight
relatedBy:0
toItem:superView
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:0.f];
NSLayoutConstraint *leading = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:0.f];
[superView addConstraint:width];
[superView addConstraint:height];
[superView addConstraint:top];
[superView addConstraint:leading];
}

Set position, height, width auto layout?

I am just switching over to auto layout and before I was using:
[Btn setFrame:CGRectMake(165,164,135,35)];
But I know that for auto layout you don't use set frame. I know it's supposed to be something like:
self.constraintToAnimate = [NSLayoutConstraint constraintWithItem:label
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:0.25
constant:0.0];
and this:
[self.scrollView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|-[Btn(==300)]-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(Btn)]];
but I am not sure what each of these do or how the constraintsWithVisualFormat string means/ how it works.
If you could show me how to set the position, height and width of the Btn I would really appreciate it.
Thanks in advance.
Ok, I have to extrapolate a bit about the layout you are trying to achieve since you didn't exactly specify it. I am assuming you have a UIScrollView with a button as a subview that complicates things.
Since you are new to constraints, let's start with a simpler case, which is a UIButton which is a subview of a UIView. A UIButton already knows what its width and height should be based on its content, so you only need to specify its x and y location (2 constraints). I personally prefer the equation form of writing constraints rather than the visual format, so I'll use that. What you want is:
button.x = button.superview.x*1 + 0;
button.y = button.superview.x*1 + 0;
.x is a leading constraint (leading is from the left) and .y is a top constraint (from the top).
Next, since the constraint is between a child and it's superview, you add the constraints to the superview
[button.superview addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:button.superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:164.0]];
[button.superview addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:button.superview attribute:NSLayoutAttributeLeading multiplier:1.0 constant:165.0]];
This would position your button at (164,165) and the width and height of your button would come from the intrinsic size of the button (based on it's image / text).
So this is the simple case. Now let's turn to the scenario where your button.superview is a UIScrollView. This is more complicated because you need to think of constraints with a UIScrollview with a different mindset, namely like it is a fixed window size (the frame) into a landscape of infinite size (contentSize).
So let's say you had a UIView --> UIScrollView --> UIButton, where the UIView has a child scrollview, which in turn has a UIButton as a subview. Take this one step at a time.
1.) UIView --> UIScrollView
Constraints you write on this pair are between the frame of the UIView and the frame of the UIScrollView. This is very easy since the frame is a fixed size. So let's say you wanted the UIScrollView to be the full size of the UIView it was embedded in. You would write 4 constraints to specify it's X,Y,width and height. There are many ways to do this of course, I will list one.
scrollview.x (scrollview.frame.x) = scrollview.superview.x*1 + 0;
scrollview.y (scrollview.frame.y) = scrollview.superview.y*1 + 0;
scrollview.width (scrollview.frame.width) = scrollview.superview.width*1 + 0;
scrollview.height (scrollview.frame.height) = scrollview.superview.height*1 + 0;
In constraints, that would be:
// Let's constrain the scrollview to stick to the top and left of it's superview
[scrollView.superview addConstraint:[NSLayoutConstraint constraintWithItem:scrollView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView.superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[scrollView.superview addConstraint:[NSLayoutConstraint constraintWithItem:scrollView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:scrollView.superview attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
// Let's constrain the scrollview to make its width and height equal to its superview
[scrollView.superview addConstraint:[NSLayoutConstraint constraintWithItem:scrollView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scrollView.superview attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
[scrollView.superview addConstraint:[NSLayoutConstraint constraintWithItem:scrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:scrollView.superview attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
Ok, now the scrollview knows how to position its frame. That was the easy part.
2.) Situate the button inside the scrollview.
Ok, now you have to change your thinking. Since the scrollView is the parent of the button, you cannot constrain to the frame of the scrollView, but rather have to constrain to the contentView of the scrollView. This is theoretically an infinite plane.
So how can you constrain a view in an infinite plane? There is no setting in UIScrollView to do such a thing, so are we stuck?
No, what we need to do is define a content view for our UIScrollView. Let's drag ANOTHER view into the UIScrollView, and place the button in that view. Our view hierarchy should look like this now.
UIView (topView) --> UIScrollView --> UIView (contentView) --> UIButton (all nested).
Now we need to set up constraints to define the size of the contentView. Let's say that the contentView is the same size as the topView. Let's add constraints to make it so. The following 4 constraints are saying that the content view is the plane of the scrollview.
[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]];
[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:scrollView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]];
But this is not enough because we said the scrollview was basically a window into an infinite plane. So we need 2 more constraints on our contentView to specify it's width and height. Let's just make them the same as the top level view. Notice that we are setting the constraints up between the contentView and the topView, bypassing the ScrollView altogether.
// You could make the constant parameter non-zero if you wanted it to be bigger/smaller (+/-) than the view
[topView addConstraint:[NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
[topView addConstraint:[NSLayoutConstraint constraintWithItem:contentView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
Now we can finally define constraints on the button, which can be written as below so you follow along, or can be written as defined at the very top, since contentView == button.superview.
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:164.0]];
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:contentView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:165.0]];
Now we should have properly set up all the constraints. To force a layout, just call [topView layoutIfNeeded] Hopefully this was clear, and let me know if you are having any problems. I might have missed something as I had to write this off the top of my head :)

Autoresize titleView in a NavigationBar with autolayout

I want a custom UIView in my UINavigationBar between a left and a right BarButtonItem but as wide as possible. For this reason I added a UIView in IB to the NavigationBar. With autolayout disabled everything works as expected with the autoresizing masks.
But in my storyboard with autolayout enabled I just cann't get it to work. It doesn't look like I can set any constraints in IB for the titleView. If I rotate my device to landscape mode, the UIView has still the same width.
What do I have to do so that the titleView fills the space between my UIBarButtonItems with autolayout enabled?
Thank you for any help
Linard
I solved the issue as following in my code:
- (void)willAnimateRotationToInterfaceOrientation:(__unused UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGSize navigationBarSize = self.navigationController.navigationBar.frame.size;
UIView *titleView = self.navigationItem.titleView;
CGRect titleViewFrame = titleView.frame;
titleViewFrame.size = navigationBarSize;
self.navigationItem.titleView.frame = titleViewFrame;
}
I haven't found another solution (with automatic resizing), but I'm open for new and/or better solutions
Linard
You can add constraints in code.
In viewDidLoad, you can do something like this:
UIView *titleView = [[UIView alloc] initWithFrame:CGRectZero];
self.navigationItem.titleView = titleView;
UIView *titleViewSuperview = titleView.superview;
titleView.translatesAutoresizingMaskIntoConstraints = NO;
[titleViewSuperview addConstraint:[NSLayoutConstraint constraintWithItem:titleView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:titleViewSuperview attribute:NSLayoutAttributeLeading multiplier:1 constant:0]]; // leading
[titleViewSuperview addConstraint:[NSLayoutConstraint constraintWithItem:titleView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:titleViewSuperview attribute:NSLayoutAttributeTop multiplier:1 constant:0]]; // top
[titleViewSuperview addConstraint:[NSLayoutConstraint constraintWithItem:titleViewSuperview attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:titleView attribute:NSLayoutAttributeWidth multiplier:1 constant:0]]; // width
[titleViewSuperview addConstraint:[NSLayoutConstraint constraintWithItem:titleViewSuperview attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:titleView attribute:NSLayoutAttributeHeight multiplier:1 constant:0]]; // height