ObjC, revert to interface builder autolayout constraints, after adding / removing programmatically? - ios

I have a free version of my app and I remove + add autolayout constraints (to hide a premium feature), however if the user purchases my app I'd like to revert back to to the constraints set by interface builder.
I'm hoping there's a method which will achieve this, but I haven't been able to find it so far?
Here's what I have
if (!purchased) {
[self.tblOtherAccounts addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[tblOtherAccounts(==0)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(tblOtherAccounts)]];
NSDictionary *views = #{ #"tblOtherAccounts" : self.tblOtherAccounts,
#"butBackAllAc" : butBackAllAc };
[self.view removeConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[tblOtherAccounts]-(12)-[butBackAllAc]"
options:0
metrics:nil
views:views]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[tblOtherAccounts]-(0)-[butBackAllAc]"
options:0
metrics:nil
views:views]];
}

Add an IBOutlet to store your constraint in your ViewController :
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *myConstraint;
Then update its value :
_myConstraint.constant = 100.0f;
[_myView setNeedsUpdateConstraints];
[_myView layoutIfNeeded];
Regarding your comment on setting the relation to "equals", I don't think you'll be able to do that.
One thing you can do though is use your storyboard-generated constraint as a placeholder :
NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:_myConstraint.firstItem
attribute:_myConstraint.firstAttribute
relatedBy:_myConstraint.relation
toItem:_myConstraint.secondItem
attribute:_myConstraint.secondAttribute
multiplier:_myConstraint.multiplier
constant:_myConstraint.constant];
[_myConstraint.secondItem removeConstraint:_myConstraint];
[constraint.secondItem addConstraint:newConstraint];
Of course the code above will create an exact duplicate of your constraint, but you can edit it to change the appropriate values to fit your needs.

I think, you cannot do exactly that.
You need to perform that logic in code. I.e. apply some constraints, then, on user action, apply another set of constraints.
Example:
You have 2 constraints:
#property ... topConstraint;
#property ... leadingConstraint;
At the start you assign them some values:
topConstraint.constant = 100;
leadingConstraint.constant = 100;
After some user action, modify them:
topConstraint.constant = 150;
leadingConstraint.constant = 150;

Related

iOS7 Autolayout and UIImageview oversized

Please see my answer below, where I have a working solution
I have rewritten the code completely so it is much smaller and neater and achieves the same results.
Update: It looks like the intrinsic content size of the UIImageView is being adjusted when the large image is loaded, which throws its layout and makes it twice the width of device window and scrollview. I will need to figure out how to fix that. The code below made a small change and reduced the width of the UIImageView, but did not allow me to set it to be 100% the width of the window.
[captionImageView setContentCompressionResistancePriority:1 forAxis:UILayoutConstraintAxisHorizontal];
I have the following set up for my views and my problem is that an image inside the UIImageView is pushing the width of its containing view to be wider than the device window.
I have a ViewController that loads and adds subviews in a for loop and everything seems to be working ok there.
Inside these sub views I am trying to use autolayout to achieve the following:
Add a UIImageView and make it the same width as the parent window
Load a UIImage into this and use UIViewContentModeScaleAspectFill to make sure the image loads nice and proportionally.
Load a UITextView beneath all this
Repeat this process in the for loop so they all stack up nicely on top of one another.
I am using a constraint for in the Visual formatting language to try to set the size of the UIImage view to be the width of the parent view, which I want to be the device window width.
If I specify the constraint as follows:
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[captionImageView(320)]|" options:0 metrics:metrics views:views]];
Then it works ok, but this is not the ideal solution as I want the UIImageView to be flush with the width of the device, and it's bad practice to set a fixed width
If I specify the constraint as follows like this:
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[captionImageView]|" options:0 metrics:metrics views:views]];
Then the UIImage with a size of let's say 960px will force the parent container view to be that size as well.
I have my custom view content being loaded into a scroll view, and so we have horizontal scrolling which is bad.
Note that I am using a UIView category to specify that view.translatesAutoresizingMaskIntoConstraints is set to NO.
#import "UIView+Autolayout.h"
#implementation UIView (Autolayout)
+(id)autoLayoutView {
UIView *view = [self new];
view.translatesAutoresizingMaskIntoConstraints = NO;
return view;
}
#end
The code for the custom view is below. Any ideas as to how I can ensure the size of my UIImageView does not stretch beyond the bounds of the device window?
//
// FigCaptionView.m
//
//
// Created by Matthew Finucane on 18/12/2014.
// Copyright (c) 2014 The App. All rights reserved.
//
#import "FigCaptionView.h"
#import "UIView+Autolayout.h"
#interface FigCaptionView(){}
#end
#implementation FigCaptionView
-(id)initWithData:(NSDictionary *)viewData {
if((self = [FigCaptionView autoLayoutView])) {
self.figCaptionData = viewData;
}
return self;
}
-(void)layoutItems {
[self setBackgroundColor:[UIColor redColor]];
/**
* The container view for everything
*/
UIView *containerView = [UIView autoLayoutView];
/**
* Setting up the caption image
*/
UIImageView *captionImageView = [UIImageView autoLayoutView];
[captionImageView setContentMode:UIViewContentModeScaleAspectFill];
/**
* Grabbing the image (not yet the right way to do this)
*/
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://placekitten.com/960/240"] options:0 error:nil]];
dispatch_async(dispatch_get_main_queue(), ^{
captionImageView.image = image;
});
});
/**
* Setting up the caption image
*/
UITextView *captionTextView = [UITextView autoLayoutView];
[captionTextView setText:#"Sample paragraph text will go in here. Sample paragraph text will go in here. Sample paragraph text will go in here. Sample paragraph text will go in here. Sample paragraph text will go in here.Sample paragraph text will go in here"];
/**
* Adding the container view
*/
[self addSubview:containerView];
[containerView addSubview:captionImageView];
[containerView addSubview:captionTextView];
[captionTextView setBackgroundColor:[UIColor blueColor]];
/**
* Dictionaries for autolayout: views and metrics
*/
NSDictionary *views = NSDictionaryOfVariableBindings(containerView, captionImageView, captionTextView);
NSDictionary *metrics = #{#"imageHeight": #"160.0", #"margin": #"20.0"};
/**
* Setting up the constraints for this view
*/
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[containerView]|" options:0 metrics:metrics views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[containerView]|" options:0 metrics:metrics views:views]];
/**
* Container view constraints
*
* The first constraint is the one that might be causing me trouble.
*/
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[captionImageView]|" options:0 metrics:metrics views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[captionTextView]|" options:0 metrics:metrics views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[captionImageView(imageHeight)][captionTextView]|" options:NSLayoutFormatAlignAllLeft metrics:metrics views:views]];
for(UIView *view in self.subviews) {
if([view hasAmbiguousLayout]) {
NSLog(#"OOPS");
NSLog(#"<%#:0x%0x>", view.description, (int)self);
}
}
}
#end
You can handle it in this way.
Assume ViewA's width is equal to the device window 's width.In this case, you can just add a UIView.
And set it up with constraints likes the usual view you had handled above.
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[captionImageView(ViewA)]|" options:0 metrics:metrics views:views]];
Hope this help.
Edit:
Cause I am not very sure it will work or not in the way that you comment to me. So I just showed the code how I deal with.
1.set up the viewA
_viewA = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
2.Just handle it likes your UIImageView (in this case, captionImageView)
[self.view addSubView:_viewA];
[_viewA setTranslatesAutoresizingMaskIntoConstraints:NO];
3.Add it into NSDictionaryBinding
NSDictionary *dictionary = NSDictionaryOfVariableBindings(_textView, _button, _viewA);
4.Custom the VFL and assign it.
NSString *const KButtonHorizontal = #"|[_button(_viewA)]|";
This is the tech note that explains how to constrain a scroll view: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
A quick and easy way to prevent the scroll view content size from growing is to subclass the scrollview and override -(CGSize)contentSize to return the windows width and whatever super returns for height.
I got this working by Subclassing UIImageView and overriding intrinsicContentSize() to return the screen size... less code ;)
class CustomImageView: UIImageView
{
// - MARK: UIView
override func intrinsicContentSize() -> CGSize
{
// Return current screen size with width adjusted for frame
let width = UIScreen.mainScreen().bounds.width - frame.origin.x
return CGSize(width: width, height: frame.height)
}
}
I would not necessarily call this a definitive answer, but I was able to get it working somewhat the way I wanted to, although the solution I went with might not have been 100%.
-- The answer provided by Henry T Kirk was very useful in describing autolayout with image views. I could not get this working 100% but I would say this is the most correct answer in that it follows best practice.
-- The answer provided by WorldOfWarcraft also works. Creating a new view that stretches to 100% the width of the device window, and setting the constraint of the UIImage based on that will stop it from stretching beyond its bounds.
The solution I went with was to create a new CGRect variable called windowBounds. Extracting the window width from that (windowBounds.size.width), I was able to add this to my metrics, and apply it to the visual formatting string horizontal rule for the imageView, thus correcting the constraint for its superview.
After doing some work to refactor the code for this view, I have come up with a much neater solution that works, and also solves another issue I had with placing a UITextView with variable string content. The code is below.
** FigcaptionView.h **
//
// FigCaptionView.m
// Anna Christoffer
//
// Created by Matthew Finucane on 18/12/2014.
// Copyright (c) 2014 Anna Christoffer. All rights reserved.
//
#import "FigCaptionView.h"
#import "UIView+Autolayout.h"
#interface FigCaptionView(){}
#property (nonatomic, strong) UIImageView *figCaptionImageView;
#property (nonatomic, strong) UITextView *figCaptionTextView;
#end
#implementation FigCaptionView
#synthesize figCaptionImageView;
#synthesize figCaptionTextView;
-(id)initWithData:(NSDictionary *)viewData {
if((self = [FigCaptionView autoLayoutView])) {
self.figCaptionData = viewData;
}
return self;
}
-(void)loadImage:(NSString *)imageURLPath {
//TODO
}
-(void)addContentViews {
[super layoutSubviews];
/**
* Set up and add the subviews to display the content
*/
self.figCaptionImageView = [UIImageView autoLayoutView];
[self.figCaptionImageView setBackgroundColor:[UIColor lightGrayColor]];
[self addSubview:figCaptionImageView];
self.figCaptionTextView = [UITextView autoLayoutView];
[self.figCaptionTextView setScrollEnabled:NO];
[self.figCaptionTextView setText:#"The digital atlas teaches the cartographic and cultural contents with a highly dynamic and visual method. The idea is based on the phenomenon of “cabinets of wonder” from the 16th and 17th century. At this time European discoverers collected during their expeditions various exotic objects and on the turn to Europe replaced the found pieces to a universal collection."];
[self addSubview:figCaptionTextView];
/**
* Then apply the constraints
*/
[self autoLayoutAddConstraints];
}
-(void)autoLayoutAddConstraints {
/**
* Any set up values that we need
*/
CGRect windowBounds = [[UIScreen mainScreen] bounds];
/**
* Dictionary of views and metrics
*/
NSDictionary *views = NSDictionaryOfVariableBindings(self, figCaptionImageView, figCaptionTextView);
NSDictionary *metrics = #{#"imageWidth": #(windowBounds.size.width), #"margin": #20.0f};
/**
* Constraints for this view (Vertical and horizontal)
*/
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[figCaptionImageView(imageWidth)]|"
options:0
metrics:metrics
views:views
]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[figCaptionImageView][figCaptionTextView]|"
options:0
metrics:metrics
views:views
]];
/**
* Constraints for the caption image view to maintain ratio
*/
[self.figCaptionImageView addConstraint:[NSLayoutConstraint constraintWithItem:self.figCaptionImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.figCaptionImageView
attribute:NSLayoutAttributeWidth
multiplier:0.75f
constant:0.0f
]];
/**
* Constraints for the caption text view - horizontal
*/
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[figCaptionTextView]|"
options:0
metrics:metrics
views:views
]];
}
#end
Incase you were also wondering, I have included the implementation for UIView+Autolayout
** UIView+Autolayout.m **
//
// UIView+Autolayout.m
// Anna Christoffer
//
// Created by Matthew Finucane on 19/12/2014.
// Copyright (c) 2014 Anna Christoffer. All rights reserved.
//
#import "UIView+Autolayout.h"
#implementation UIView (Autolayout)
+(id)autoLayoutView {
UIView *view = [self new];
view.translatesAutoresizingMaskIntoConstraints = NO;
return view;
}
#end
You can try:
[captionImageView setClipsToBounds:YES];
try it right after you set aspect mode...
this might help: Crop UIImage to fit a frame image
EDIT
I just said about clipToBounds because even using autolayout from interface builder i had problems with my images in AspectFill mode, they would not respect the bounds of the imageview. But seems like your problem has more to do with layout constraints.
Try these constraints (notice the - sign next to the superview denoted as pipe |), it just adds the default aqua space to the constraints, it's simple but may work:
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[captionImageView]-|" options:0 metrics:metrics views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[captionTextView]|" options:0 metrics:metrics views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[captionImageView(imageHeight)][captionTextView]|" options:NSLayoutFormatAlignAllLeft metrics:metrics views:views]];

autolayout's visual format does not work with iOS8

I have a simple ViewController that contains only its default view with a background image placed on it, I have the following updateViewConstraints method which works perfectly on iOS 7 devices (without any call to setNeedsXXX methods):
-(void) updateViewConstraints {
[super updateViewConstraints];
[self.view removeConstraints:self.view.constraints];
NSDictionary *viewsDict = #{#"bgImage":self.bgImageView};
NSArray * constraints;
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[bgImage]|" options:0 metrics:nil views:viewsDict];
[self.view addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[bgImage]|" options:0 metrics:nil views:viewsDict];
[[self.view addConstraints:constraints];
}
however, this method does not work as expected on iOS 8 devices, the background image is shifted up and didn't fit in place.
any ideas would be highly appreciated.
This line: [self.view removeConstraints:self.view.constraints]; removes more on iOS 8 than on iOS 7. The easies way to fix this it to add an array holding the constraints you add to the view and remove only those constraints.
Maybe you even can remove this line completely but this depends on your application.

Auto layout - complex constraints

I'm trying to display content in a table cell using auto layout, programmatically. I'd like for the content to display as follows:
[title]
[image] [date]
[long string of text, spanning the width of the table, maximum of two lines]
My code looks like this:
-(NSArray *)constraints {
NSMutableArray * constraints = [[NSMutableArray alloc]init];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_titleLabel, _descriptionLabel, _dateLabel, _ratingBubbleView);
NSDictionary *metrics = #{#"padding":#(kPadding)};
NSString *const kVertical = #"V:|-(>=0,<=padding)-[_titleLabel]-(<=padding)-[_ratingBubbleView]-(<=padding)-[_descriptionLabel]-(>=0,<=padding)-|";
NSString *const kVertical2 = #"V:|-(>=0,<=padding)-[_titleLabel]-(<=padding)-[_dateLabel]-(<=padding)-[_descriptionLabel]-(>=0,<=padding)-|";
NSString *const kHorizontalDescriptionLabel = #"H:|-padding-[_descriptionLabel]-padding-|";
NSString *const kHorizontalTitleLabel = #"H:|-padding-[_titleLabel]";
NSString *const kHorizontalDateLabel = #"H:|-padding-[_ratingBubbleView]-padding-[_dateLabel]";
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:kVertical options:0 metrics:metrics views:viewsDictionary]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:kVertical2 options:0 metrics:metrics views:viewsDictionary]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:kHorizontalDescriptionLabel options:0 metrics:metrics views:viewsDictionary]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:kHorizontalTitleLabel options:0 metrics:metrics views:viewsDictionary]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:kHorizontalDateLabel options:0 metrics:metrics views:viewsDictionary]];
return constraints;
}
This is the result:
OK, I'm not going to try a fix your code. I'm just going to create constraints that I would use to achieve your layout. I'll put the thought process in comments.
First get a nice vertical layout going...
// I'm just using standard padding to make it easier to read.
// Also, I'd avoid the variable padding stuff. Just set it to a fixed value.
// i.e. ==padding not (>=0, <=padding). That's confusing to read and ambiguous.
#"V:|-[titleLabel]-[ratingBubbleView]-[descriptionLabel]-|"
Then go through layer by layer adding horizontal constraints...
// constraint the trailing edge too. You never know if you'll get a stupidly
// long title. You want to stop it colliding with the end of the screen.
// use >= here. The label will try to take it's intrinsic content size
// i.e. the smallest size to fit the text. Until it can't and then it will
// break it's content size to keep your >= constraint.
#"|-[titleLabel]->=20-|"
// when adding this you need the option "NSLayoutFormatAlignAllBottom".
#"|-[ratingBubbleView]-[dateLabel]->=20-|"
#"|-[descriptionLabel]-|"
Try not to "over constrain" your view. In your code you are constraining the same views with multiple constraints (like descriptionLabel to the bottom of the superview).
Once they're defined they don't need to be defined again.
Again, with the padding. Just use padding rather than >=padding. Does >=20 mean 20, 21.5, or 320? The inequality is ambiguous when laying out.
Also, In my constraints I have used the layout option to constrain the vertical axis of the date label to the rating view. i.e. "Stay in line vertically with the rating view". Instead of constraining against the title label and stuff... This means I only need to define the position of that line of UI once.

Shifting UIViews with Auto Layout when one ore more views are hidden

I have a view that has an header. This header has 4 views that show images on the right. I call them icons since every of them shows or draw a glyph. Depending on data, icon 2, 3 or 4 may be hidden given me six possible combinations.
Even when hidden, every invisible icon occupy its space, giving one or more "holes" in the visualization.
This is what I'm using right now.
[header addSubview:_label];
[header addSubview:_icon1];
[header addSubview:_icon2];
[header addSubview:_icon3];
[header addSubview:_icon4];
NSDictionary *headerViewDict = NSDictionaryOfVariableBindings(_label, _icon1, _icon2, _icon3, _icon4);
[header addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-2-[_label]-0-[_icon4(>=0,14)]-1-[_icon3(>=0,14)]-1-[_icon2(>=0,14)]-1-[_icon1(14)]-2-|" options:nil metrics:nil views:headerViewDict]];
[header addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_label]|" options:nil metrics:nil views:headerViewDict]];
[header addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_icon1]|" options:nil metrics:nil views:headerViewDict]];
[header addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_icon2]|" options:nil metrics:nil views:headerViewDict]];
[header addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_icon3]|" options:nil metrics:nil views:headerViewDict]];
[header addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_icon4]|" options:nil metrics:nil views:headerViewDict]];
I've just read (https://stackoverflow.com/a/18066138/1360888) that to solve this there are two possibilities over-constrain or change constant.
I'm quite new to autolayout and I have used always Visual Format Language (since I build my view with code only) so I did not understood how to apply that solution to my case.
How can I create a fluid layout for my view?
Note: In my app I have a lot of views like the header visible at the same time, so performance is important.
What I would do is to create an array of icons(as you call them, UIImageViews?). Then update the contents of the array according to your data.
On ViewWillLayoutSubviews
Check the array [1, 2, 3, 4]
Remove all constraints
Set new constraints according to your content array. The important thing here is to check the element of the array as nil. Autolayout does not process the nil elements correctly and fails.
Removing constraints:
//Clear the constraints
for (NSLayoutConstraint *constraint in [self.view constraints]) {
[self.view removeConstraint:constraint];
}
Adding constraints:
if (myCustomView) {//constraintsWithVisualFormat does not support handling nil
//Add constraints for myCustomView Here
}
ViewWillLayoutSubviews
/**
* Update the constraints before laying subviews
*
*/
- (void) viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
//Remove constraints
//Set new constraints
}

'NSInvalidArgumentException', reason: 'Unable to parse constraint format'

I have a subview that I want to keep stops during rotating screen, so I decided to put the NSLayoutConstraint type:
Trailing Space to Superview
Top Space to Superview
Button Space to Superview
I'm in a subclass of UITableViewCell. I wrote the code but I get the following error:
'NSInvalidArgumentException', reason: 'Unable to parse constraint format:
self is not a key in the views dictionary.
H:[self.arrows]-5-|
My code in CustomCell.m is:
self.arrows = [[Arrows alloc]initWithFrame:CGRectMake(self.contentView.bounds.size.width-30, self.bounds.origin.y+4, 30, self.contentView.bounds.size.height-4)];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(self.arrows, self.contentView);
NSMutableArray * constraint=[[NSMutableArray alloc]init];
[constraint addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"H: [self.arrows]-5-|" options:0 metrics:nil views:viewsDictionary]];
[constraint addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-1-[self.arrows]" options:0 metrics:nil views:viewsDictionary]];
[constraint addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"[V: [self.arrows]-1-|" options:0 metrics:nil views:viewsDictionary]];
[self.arrows addConstraints:constraint];
It looks like that the autolayout visual format parsing engine is interpreting the "." in your VFL constraint to be a keyPath instead of a key like it's using valueForKeyPath:.
NSDictionaryOfVariableBindings(...) will take whatever your parameter is in the parenthesis and translate it into a literal key with the object as the value (in your case: #{"self.arrow" : self.arrow}). In the case of the VFL, autolayout is thinking that you have a key named self in your view dictionary with a subdictionary (or subobject) that has a key of arrow,
#{
#"self" : #{ #"arrow" : self.arrow }
}
when you literally wanted the system to interpret your key as "self.arrow".
Usually, when I'm using a instance variables getter like this, I typically end up creating my own dictionary instead of using NSDictionaryOfVariableBindings(...) like so:
NSDictionary *views = #{ #"arrowView" : self.arrow }
or
NSDictionary *views = NSDictionaryOfVariableBindings(_arrow);
Which would allow you to use the view in your VFL without the self and you still know what you're talking about:
NSArray *arrowHorizConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[arrowView]-5-|" options:0 metrics:nil views];
or
NSArray *arrowHorizConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[_arrow]-5-|" options:0 metrics:nil views];
As a general rule, I've learned not to have dictionary keys with a dot (.) in them to avoid any system confusion or debugging nightmares.
My trick is to simply declare a local variable that's just another pointer to the property, and put it in the NSDictionaryOfVariableBindings.
#interface ViewController ()
#property (strong) UIButton *myButton;
#property (strong) UILabel *myLabel;
#end
...
UIButton *myButtonP = self.myButton;
UILabel *theLabelP = self.myLabel;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(myButtonP, myLabelP);
The P suffix is for "pointer".
Easiest solution is to avoid the getters for variables from your own class and redefine variables from superclasses as local variables. A solution for your example is
UIView *contentView = self.contentView;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_arrows, contentView);
Make sure you add the constraints after adding the required subview to your main view.It took a while get knowledge relating to this issue .

Resources