iOS 8 - Find constraints for a UIView with identifier - ios

I am trying to change a constraint for an ImageView for iPhone 4S,5,5S in viewDidLoad:
for (NSLayoutConstraint *constraint in logoImage.constraints) {
if ([constraint.identifier isEqualToString:#"logoTopIdentifier"]) {
constraint.constant=10;
}
}
It seems that it's not even iterating in the loop. Is there any other way to get a specific constraint with identifier?

You can connect the constraint in storyboard to your view controller class same as connecting a UI element.
Just find the constraints in your storyboard, make the workspace into split view, and drag the constraint to your corresponding view controller class.
Sometimes if you want to animate the position change, you can update the constraint like:
self.theConstraint?.constant = 100
self.view.setNeedsUpdateConstraints()
UIView.animateWithDuration(0.7) { () -> Void in
self.view.layoutIfNeeded()
}
block.
That is it.

Here is the KVConstraintExtensionsMaster library by which you can access the any constant from a view based on the NSLayoutAttribute. No matter whether that constraint added Programmatically or from Interface Builder.
[self.logoImage accessAppliedConstraintByAttribute:NSLayoutAttributeTop completion:^(NSLayoutConstraint *expectedConstraint){
if (expectedConstraint) {
expectedConstraint.constant = 10;
/* for the animation */
[self.logoImage updateModifyConstraintsWithAnimation:NULL];
}
}];

Related

How to set constant property in code behind to view using xamarin IOS

I have one view controller in Xamarin form, In that I have created one button and view assigned a height and width for view. In application execution I have hide the view using constant=-100(width). while click the button i want to show the view using Constant=0. But its not working showed error like UIView does not contain a definition for 'Constant' and no extension method 'Constant' accepting a first argument of type UIView could be found (are you missing a using directive or an assembly reference?).
Constant is not a property of a control, It's a property of NSLayoutConstraint.
I have one view controller in Xamarin form
I'm confused about whether you are working on Xamarin.Forms or Xamarin.iOS? UIViewController and UIView are components of Xamarin.iOS.
If you want to change the UIView's Width Constraint in Xamarin.iOS using code behind, you can try:
foreach(NSLayoutConstraint constraint in sidemenu.Constraints)
{
if (constraint.FirstItem == sidemenu && constraint.FirstAttribute == NSLayoutAttribute.Width)
{
constraint.Constant = 190;
break;
}
}
Moreover if you are using Storyboard, you can reference the sidemenu's Width Constraint to the corresponding UIViewController as an outlet. Then Modify it:
// WidthConstraint is an outlet of sidemenu's width constraint
WidthConstraint.Constant = 190;

No Constraints found in viewDidLayoutSubviews - AutoLayout

Happy Thanksgiving if you celebrate it!
I have a UIImageView on storyboard, setup with 4 constraints. The center x constraint has an identifier set (via storyboard), "imageViewTwoCenterX".
I'm trying to find that constraint with the identifier.
PROBLEM: The code below returns 0 for the constraints array count, and never finds the constraint with the identifier.
Am I doing something wrong? Wrong practice? All help is appreciated!
I use this code:
override func viewDidLayoutSubviews() {
print("Constraints Count: \(imageViewTwo.constraints.count)")
for constraint in imageViewTwo.constraints {
if constraint.identifier == "imageViewTwoCenterX" {
print("Found it!")
}
}
}
For constraints other than width/height constraints, IB adds them to the view's superview. So you won't find them among the image view's constraints. Try listing its superview's constraints instead.

Show and hide views when using AutoLayout

I have some views where I have to remove a UIView and let all the other views rearrange without this view. I usually do this by creating multiple constraints with different weights, so when I removeFromSuperView all the other constraints with lower weight start working and everything rearranges as I want.
It works perfectly but now I have to show and hide the view but when I removeFromSuperView, that view is gone with all its constraints... I have no idea what to do... I can change width and heights constraints to zero for the view I want to hide... but the problem with this is that then I have to hard code the height in the code and I don't like it...
What's the best way of hiding and showing UIViews? Something like the "Installed" property that you can set in the IB but from the code.
When you remove the view from super view you lose the constraint, you're basically making it available for any object in your view and that way you have to write some methods to add to other views (with programmatically adding constraint) or removing it again.
From my experience to hide view you can use constraint animation or view itself animation and throw it off to left, right, up or down. Also for it to not be available within user interaction you can just disable it with self.view.hidden = true or self.view.userInteractionEnabled = false. use dispatch_async() when you also updating view, apple love people who code standard :).
Hide View itself with animation:
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.view.hidden = true // Or userInteractionEnable = false
UIView.animateWithDuration(1.0, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.reportView.alpha = 0
}, completion: nil)
})
Hide View with Constraint:
You need to create constraint IBOutlet to use the constant with animation.
self.someView.constant = -205 // Throw it off canaves
UIView.animateWithDuration(1.0, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: { (finished: Bool)-> Void in
self.someView.hidden = true
})
To make it appear just do the same think in reverse. but don't removeFromSuperView(), unless you trying to attach it to First Responder or something like that.
If targeting iOS 9 and above, you can try UIStackView.
One way we can think of preserving constraints is by creating a category on UIView.
#interface UIView (ConstraintsPreserving)
#property (nonatomic, strong) NSMutableArray * preservedConstraints;
-(void)removeFromSuperViewPreservingConstriants;
-(void)addToSuperViewPreservingConstriants:(UIView *)superView;
#end
#import <objc/runtime.h>
#implementation UIView (ConstraintsPreserving)
#dynamic preservedConstraints;
-(void)setPreservedConstraints:(NSMutableArray *)object {
objc_setAssociatedObject(self, #selector(preservedConstraints), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSMutableArray *)preservedConstraints {
return objc_getAssociatedObject(self, #selector(preservedConstraints));
}
-(void)removeFromSuperViewPreservingConstriants
{
if(!self.superview) return;
self.preservedConstraints = [NSMutableArray new];
[self.preservedConstraints addObjectsFromArray:[self constraints]];
for (NSLayoutConstraint * constraint in self.superview.constraints) {
if([constraint.firstItem isEqual:self] || [constraint.secondItem isEqual:self])
[self.preservedConstraints addObject:constraint];
}
[self removeFromSuperview];
}
-(void)addToSuperViewPreservingConstriants:(UIView *)superView
{
if(self.superview) return;
[superView addSubview:self];
[superView addConstraints:self.preservedConstraints];
self.preservedConstraints = nil;
}
#end
PS: This strongly captures the constraints, there by capturing all the view involved in these constraints.
I am not sure if appropriate to your problem, but what I did a couple of times, was to make sure that the view you want to hide is in a container. Such container should have a height and width constraint (and this is where you might not like it). You then create IBOutlets for each one of these constraints in the relevant view controller (control drag from the constraint within storyboard). Finally you can save the initial value of the constraint in viedidload, use it later to switch from zero to that value, whatever it is.
So, I ended up creating creating IBOutlets to the views I want to hide. I set the width and height constraints to zero that way I hide the view. Also I set multiple IBOutlets and changed the priority of this constraints so I can re arrange stuff...
That's the best way I found of doing it.

Find in code a layout constraint created in IB

I would like to animate, in code, a constraint that is created in IB. I want to find the constraint between the bottom LayoutGuide and the bottom edge of my UIView.
The constraint instances are remarkably opaque: firstItem and secondItem are AnyObject so there is a lot of casting. And apart from doing a string compare on _stdlib_getTypeName(), it's hard to see how I will decide which constraints involve the LayoutGuides.
Or should I just delete all constraints and re-create them on the fly? (But then what's the point of IB? Since my storyboard uses auto layout, I am obliged to add constraints in IB anyway.)
Click on the constraint in Interface Builder and create an IBOutlet for it, just as you would for a button, text view, etc.
You can also find it at runtime using something like this:
NSLayoutConstraint *desiredConstraint;
for (NSLayoutConstraint *constraint in putYourViewHere.constraints) {
if (constraint.firstAttribute == NSLayoutAttributeHeight) { // Or whatever attribute you're looking for - you can do more tests
desiredConstraint = constraint;
break;
}
}
This should be simple. As has already been stated, create an IBOutlet for the constraint you want to animate:
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *verticalSpaceLayoutConstraint;
Then when you want to animate it (Also see: How do I animate constraint changes?) force a layout pass if needed in your view that has the constraint, update your constraint to value you want, do your UIView animation and force layout passes as needed for the animation's duration:
- (void)moveViewSomewhere {
[self.view layoutIfNeeded];
_verticalSpaceLayoutConstraint.constant = 10; // The value you want your constraint to have when the animation completes.
[UIView animateWithDuration:1.0
animations:^{
[self.view layoutIfNeeded];
}];
}

Is there a way to add an identifier to Auto Layout Constraints in Interface Builder?

After doing some visual layout in Interface Builder I've created some constraints that I want to access at runtime. Is there a way to label or identify constraints in Interface Builder so they can be looked-up later?
The reason I want to do this is I need to perform some calculation base upon the constraints that are visually specified. I am aware that Apple has provided the Visual Format Language and I could specify the constraints programmatically in a controller. I rather not use this approach so I don't lose the design time feedback of IB.
Edit
Making a referencing outlet did work, but the question still stands.
Update:
As explained by Bartłomiej Semańczyk in his answer, there is now an Identifier field visible in the Attributes Inspector for the NSLayoutConstraint making it unnecessary to expose this field yourself. Just select the constraint in the Document Outline view or in the Storyboard view and then add an identifier in the Attributes Inspector on the right.
Earlier Answer:
Yes, this can be done. NSLayoutConstraint has a property called identifier than can be exposed in Interface Builder and assigned. To demo this, I created a Single View Application that has a single subview that is a red box. This subview has 4 constraints: width, height, centered horizontally in container, centered vertically in container. I gave the width constraint the identifier redBoxWidth by doing the following:
Click on the width constraint in the Document Layout View. Then in the Identity Inspector under User Defined Runtime Attributes, click on the + under Key Path. Change keyPath to identifier, change the Type Boolean to String, and set the Value to redBoxWidth.
Then in ViewDidLoad it is possible to find the constraint by name and change its value:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
for subview in view.subviews as [UIView] {
for constraint in subview.constraints() as [NSLayoutConstraint] {
if constraint.identifier == "redBoxWidth" {
constraint.constant = 300
}
}
}
}
}
Since Xcode 7 you can do it in storyboard:
However if you set up your constraint in code, do the following:
let constraint = NSLayoutConstraint()
constraint.identifier = "identifier"
For these constraints you have set up in storyboard, but you must set identifier in code:
for subview in view.subviews {
for constraint in subview.constraints() {
constraint.identifier = "identifier"
}
}
Also you could link constraint to properties of your controller as you do for any other components. Just ctrl drag it into your code:
And then it will be accessible in code of your controller as property:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *myConstraint;
And you could change it value for example:
self.myConstraint.constant=100.;
Just adding on to #vacawama's answer. You can write a UIView category and pull out constraints using a simple function, as opposed to copy/pasting loops everywhere you need to find a constraint:
.h file:
#import <UIKit/UIKit.h>
#interface UIView (EasyAutolayout)
-(NSLayoutConstraint *)constraintForIdentifier:(NSString *)identifier;
#end
.m file:
#import "UIView+EasyAutolayout.h"
#implementation UIView (EasyAutolayout)
-(NSLayoutConstraint *)constraintForIdentifier:(NSString *)identifier {
for (NSLayoutConstraint *constraint in self.constraints) {
if ([constraint.identifier isEqualToString:identifier]) {
return constraint;
}
}
return nil;
}
#end
Take the IBOutlet of your auto layout constraint.
There is one property called constant in the NSLayoutConstraint class.
For eg, you've taken the IBOutlet of height constraint of any of the views from your IB, and you want to change it's height programmatically, all you need to do is:
constraint.constant = isMoreHeight ? height1 : height2;
After doing this, you need to update all other views in the view hierarchy of the superview. To do this you'll need to write below line:
[self setLayoutIfNeeded];
For better user experience, you can put this line inside your animations block for smoother transition effects,
[UIView animateWithDuration:0.3f animations:^{
[self.view layoutIfNeeded];
}];
Hope this helps..
What about this:
if let myconstraint = self.view.constraints.filter( { $0.identifier == "myconstraintId" }).first {
// TODO: your code here...
}
my two cents for OSX (previous answers deal with UIKit..)
it work for OSX, too:
final private func getConstraintForButton(){
let constraints = self.myButton.constraints
for constraint in constraints {
if constraint.identifier == "redBoxWidth" {
constraint.constant = 300
break
}
}
}
Note: seems NOT working on custom NSView, instead...

Resources