Here is how i have configured the profile view,
-(void)addProfileView
{
if(!_profileView)
{
_profileView = [ProfileView new];
_profileView.clipsToBounds = YES;
_profileView.delegate=self;
_profileView.alpha = 1.0;
_profileView.opaque = YES;
_profileView.translatesAutoresizingMaskIntoConstraints = false;
[self.view addSubview:_profileView];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:_profileView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:160];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:_profileView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:_profileView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:_profileView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:-64];
[NSLayoutConstraint activateConstraints:@[heightConstraint, widthConstraint, leftConstraint, topConstraint]];
}
}
Now, why is the profile view's height not changing after the animation? Should I add the constraints to the superview? What's the difference between adding constraints directly to the superview and using [NSLayoutConstraint activateConstraints:@[]] ?
-(void)animateProfileViewBasedOnOffset:(BOOL)isTop
{
__block NSLayoutConstraint *heightConstraint;
for (NSLayoutConstraint * constraint in [_profileView constraints]) {
if (constraint.firstAnchor == NSLayoutAttributeHeight) {
heightConstraint = constraint;
break;
}
}
[UIView animateWithDuration:0.3 animations:^{
if (isTop) {
heightConstraint.constant = 64;
}
else {
heightConstraint.constant = 160;
}
[_profileView layoutIfNeeded];
[self.view layoutIfNeeded];
}completion:^(BOOL completion)
{
NULL;
}];
}
The height of the profileView only changes when I add its constraint to the superview ([self.view addConstraints:@[]]). During the animation, I loop through all the constraints of the superview to find the specific constraint and update it.
You should check and confirm that you are, in fact, finding the Height constraint:
for (NSLayoutConstraint * constraint in [_profileView constraints]) {
if (constraint.firstAnchor == NSLayoutAttributeHeight) {
heightConstraint = constraint;
break;
}
}
if (heightConstraint) {
NSLog(@"Found Height Constraint: %@", heightConstraint);
} else {
NSLog(@"Did NOT find Height Constraint!");
}
That code (in my quick test) does NOT find the Height constraint, so of course, no animation.
This code:
for (NSLayoutConstraint *constraint in _profileView.constraints) {
// Check if the constraint is a height constraint
if (constraint.firstAttribute == NSLayoutAttributeHeight && constraint.firstItem == _profileView) {
// Found the height constraint
heightConstraint = constraint;
break;
}
}
if (heightConstraint) {
NSLog(@"Found Height Constraint: %@", heightConstraint);
} else {
NSLog(@"Did NOT find Height Constraint!");
}
Does find the Height constraint, and the animation happens as expected.
As a side note, you might want to look at the more modern way of setting up constraints. This matches your constraints, but I think you'd agree it is much more readable (and much easier to edit when needed):
[NSLayoutConstraint activateConstraints:@[
[_profileView.heightAnchor constraintEqualToConstant:160.0],
[_profileView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor],
[_profileView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
[_profileView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:-64.0],
]];
Edit
As mentioned in comments by Matt, you are (almost always) better off adding a constraint property that you can update when desired.
Here's an example of that approach:
#import <UIKit/UIKit.h>
@interface AnimSubViewController : UIViewController
@end
@interface AnimSubViewController ()
@property (strong, nonatomic) UIView *profileView;
@property (strong, nonatomic) NSLayoutConstraint *heightConstraint;
@end
@implementation AnimSubViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
[self addProfileView];
}
-(void)addProfileView
{
if(!_profileView)
{
//_profileView = [ProfileView new];
_profileView = [UIView new];
_profileView.backgroundColor = UIColor.redColor;
_profileView.clipsToBounds = YES;
//_profileView.delegate=self;
_profileView.alpha = 1.0;
_profileView.opaque = YES;
_profileView.translatesAutoresizingMaskIntoConstraints = false;
[self.view addSubview:_profileView];
// set the _heightConstraint property, so we can modify its .constant later
_heightConstraint = [_profileView.heightAnchor constraintEqualToConstant:160.0];
[NSLayoutConstraint activateConstraints:@[
//[_profileView.heightAnchor constraintEqualToConstant:160.0],
_heightConstraint,
[_profileView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor],
[_profileView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
[_profileView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:-64.0],
]];
}
}
-(void)animateProfileViewBasedOnOffset:(BOOL)isTop
{
[UIView animateWithDuration:0.3 animations:^{
if (isTop) {
self->_heightConstraint.constant = 64;
}
else {
self->_heightConstraint.constant = 160;
}
// not needed
//[self->_profileView layoutIfNeeded];
[self.view layoutIfNeeded];
} completion:^(BOOL completion) {
NULL;
}];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// hide/show _profileView on tap anywhere
[self animateProfileViewBasedOnOffset:_heightConstraint.constant == 160.0];
}
@end