iOS: Autolayout: Lock new position after animating - ios

I tried to write a category function for UIView that will get the constraints that will hold an given frame in place in its superview. I implemented it like so:
-(NSArray *)constraintsForLockingPositionInSuperview
{
NSLayoutConstraint *left=[NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.superview
attribute:NSLayoutAttributeLeft multiplier:0 constant:self.frame.origin.x];
NSLayoutConstraint *height=[NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:Nil
attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:self.frame.size.height];
NSLayoutConstraint *width=[NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:Nil
attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:self.frame.size.width];
NSLayoutConstraint *top=[NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.superview
attribute:NSLayoutAttributeTop multiplier:0 constant:self.frame.origin.y];
return #[left,height,width,top ];
}
And then after animating the view, apply to new constraints by doing the following in the view controller that contains the view in question. I remove the view then re-add it to remove the constraints that were on it, then reapply the new constraints. The hope was that this would hold the view in place if I add another subview or if something like an actionView comes in and the view has to layout itself out. The height and the width seem to be locking properly, but the view is jumping to the middle instead of locking in place:
NSArray *lockingConstraints = [someView constraintsForLockingPositionInSuperview];
[someView removeFromSuperview];
[self.view addSubview:someView;
[self.view addConstraints:lockingConstraints];
[self.view layoutSubviews];

From the documentation for layoutSubviews:
You should not call this method directly.
Call layoutIfNeeded instead.

Related

Subview visible but frame outside superview frame

I have a parent VC that loads a child VC inside it. Code in parent VC is as follows:
self.draggerViewController = [[DraggerViewController alloc] initWithSuperView:self.view];
And the code of the child VC is as follows:
- (instancetype)initWithSuperView:(UIView*)superView {
self = [super init];
if (self) {
self.superView = superView;
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
[superView addSubview:self.view];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:0]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1 constant:0]];
self.constraintDraggerAttributeBottom = [NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[superView addConstraint:self.constraintDraggerAttributeBottom];
}
return self;
}
With this code, the subview is visible on its parent view and constraints are applied correctly so that it is placed at the bottom of the parent view with leading and trailing being 0. BUT, the real frame of the view is outside the parent view.
That is, I can see the subview at the bottom of the parent view but the frame of this subview is frame = (0 -315; 768 35).
If I set 'clipToBounds' to 'YES', the view is placed on the real frame but now constraints are not applied correctly.
How can I place this child VC inside the parent VC's view at the position I want using constraints?
Thank you!
OK, got it...
I was forgetting the height constraint of the view... What a shame...
[superView addConstraint:[NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1 constant:0]];
Thank you all!
Try this :
superView.insertSubview(self.view, at: self. superView.subviews.count)

Programmatically add constraint to view relative to a sibling

I am using Auto Layout. I have a view with a subview (sb1). I am programmatically adding another subview (sb2) to this view. I am adding constraints to this new subview.
What I want accomplish:
add sb2 below sb1 an add a constraint to the top of sb2 to pin it to sb1.
What I've tried:
[self.containerView insertSubview:sb1
belowSubview:sb2];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:sb1
attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
toItem:sb2 attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.f];
[self.otherLeftView addConstraint:constraint];
What is happening is my newly added subview is being pinned to the top of the container view, NOT to the sb1 (which is what I want to happen). Visual of what is happening:
Does anyone know how I can add a constraint between SB1 and SB2? Thanks!
Try this:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:sb1
attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
toItem:sb2 attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.f];

Programmatically using constraints to center a UIIMageView

I'm working on an iOS project in which I need to programmatically use constraints for my views. I'm used to storyboards, but am using xibs for this particular project due to the nature of the project specification.
I have a MainViewController, in which I create the following properties in the .h file:
#property (nonatomic, strong) IBOutlet UIImageView *backgroundImageView;
#property (nonatomic, strong) IBOutlet UIImageView *logoImage;
I added these UIImageView instances to my XIB file, and selected the appropriate images via the Attributes inspector.
In my .m file, I have a addConstraints method, which is called in viewDidLoad. Inside this method, I make sure to turn off converting autoresizingMasks into constraints:
self.backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO;
self.logoImage.translatesAutoresizingMaskIntoConstraints = NO;
Then, I've set up constraints so that the background image takes up the entirety of the superview:
id mainView = #{#"background": self.backgroundImageView};
//Set constraints so that the background takes up the entirety of the superview
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[background]|" options:0 metrics:nil views:mainView]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[background]|" options:0 metrics:nil views:mainView]];
Finally, I set constraints so that the logo view is in the center of the view (which is where I am going wrong):
// Width constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// Height constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
// Center horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
// Center vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
The error I receive is
*** +[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]:
Constraint items must each be an instance of UIView or subclass'
, which I don't fully understand, as my constraint items (two UIImageView instances) are either subclasses of UIView, but I may be misunderstanding this. Can anyone help point out where I'm going wrong?
Try that :
// Width constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// Height constraint
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
// Center horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
// Center vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.logoImage
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
You were trying to define constraints between your UIImageView and your view controller. You must define constraints between views which are subviews of a same view.

Constraint added programmatically to a xib view not working

I have a view(self.printSettingsView) created from xib. I add this view as a subview to another view(self.view). I programmatically add constraints as follows:
[self.printSettingView setTranslatesAutoresizingMaskIntoConstraints: NO];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
[self.view addConstraint:leftConstraint];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.topBar
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
[self.view addConstraint:topConstraint];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
[self.view addConstraint:heightConstraint];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.printSettingView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
[self.view addConstraint:widthConstraint];
All the other constraints take effect except for height.
What could i be doing wrong here???
Thanks
Without knowing the constraints on your xib file or what you expect to happen vs what is happening (a screenshot would be helpful) it's hard to say. However I do have one suggestion where maybe the logic isn't correct.
The second constraint is pinning printViewSettings top to the bottom of the topBar, that part makes sense. The next one however sets the height of printViewSettings to the height of its superview. This may not jive with what you want because your superview contains your topBar as well and so may be larger than you expect. What you might actually want is a constraint that pins the bottom of printViewSettings to the bottom of the superview instead.
Sorry guys about the incomplete information. The problem in a gist was that i was assigning a constraint to the subview in my main view and whatever be the constant of the constraint the size of the subview remained constant. I found that the issue was the subview in turn had components(subviews) with fixed height constraints. I made them proportional to the height of the parent view and it works now.

Autolayout: Adding a view break my otherwise working resizing - ideas?

In my app, I have the following setup:
TextView (self.textView)
Toolbar
When the keyboard become visible, I add a constraint that pushes the bottom of the text view up with the required number of pixels.
spacer =[NSLayoutConstraint
constraintWithItem:self.textView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-height];
[self.view addConstraint:spacer];
When the keyboard disappears, I remove the constraint.
THIS WORKS FINE. However...
I want to add an imageview that lies on top of the text view. This seems straightforward. But now the "dismiss keyboard" resize is broken.
Here's the code I use to create the imageview, and pin it to the textview bounds.
self.overlay = [[UIImageView alloc] init];
[self.overlay setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.overlay];
[self.view addConstraint: [NSLayoutConstraint
constraintWithItem:self.overlay
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.textView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0]];
[self.view addConstraint: [NSLayoutConstraint
constraintWithItem:self.overlay
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.textView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0]];
[self.view addConstraint: [NSLayoutConstraint
constraintWithItem:self.overlay
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.textView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0]];
[self.view addConstraint: [NSLayoutConstraint
constraintWithItem:self.overlay
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.textView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0]];
[self.view layoutIfNeeded];
Here's how it should look:
Initial before keyboard showing
Keyboard showing
Keyboard removed. Layout should be back to initial state, but instead I get this
Solution: Set the content hugging priority for the tool bar to 1.000 to avoid that it gets all stretched out.

Resources