Frage

If I assign a UIView to an NSDictionary and then try to recall its value in autolayout when setting constraints as here

UILabel *label = [[UILabel alloc] init];
NSDictionary *items = [[NSMutableDictionary alloc] init];
[items setValue:label forKey:@"label"];

[NSLayoutConstraint constraintsWithVisualFormat:@"|-[label]-|"
                                   options:0
                                   metrics:nil
                                   views:NSDictionaryOfVariableBindings(
                                      [items objectForKey:@"label"]
                                    )];

I get the following error

uncaught exception 'NSInternalInconsistencyException', reason: 'NSDictionaryOfVariableBindings failed because either one of the values is nil, or there's something wrong with the way the macro is being invoked.  Cannot assign value nil for key "objectForKey:@"label"]". Keys:(
"[items",
"objectForKey:@\"label\"]")'

I get the same thing if I try to pass an array objectAtIndex: element in also

Frustratingly it does work if I define it like this

[items setValue:NSDictionaryOfVariableBindings(label) forKey:@"label"];

But I'm trying to load my UIViews into a collection object (any collection object) and then be able to recall them when setting constraints

It's as though NSDictionaryOfVariableBindings is interpreting the literal text I'm passing rather than evaluating the statement

War es hilfreich?

Lösung

It's as though NSDictionaryOfVariableBindings is interpreting the literal text I'm passing rather than evaluating the statement

This is exactly what is happening. If you command-click on NSDictionaryOfVariableBindings, you will see its definition as a macro that calls a private(ish) UIKit function in NSLayoutConstraint.h:

#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
UIKIT_EXTERN NSDictionary *_NSDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...) NS_AVAILABLE_IOS(6_0); // not for direct use

The @"" # __VA_ARGS__ in the macro definition turns the list of passed arguments into a literal string, which is passed as the commaSeparatedKeysString parameter to the _NSDictionaryOfVariableBindings function.

The correct way to use NSDictionaryOfVariableBindings is to use locally-scoped variables, or _-prefixed ivars if applicable. If you don’t want to do that, you can always make your own dictionary, using whatever literal string you want for the key and whatever value you want as the value. I often do this to rename my variables to something meaningful for the visual format language string. So, in your example:

UILabel *label = [[UILabel alloc] init];
NSMutableDictionary *items = [[NSMutableDictionary alloc] init];
[items setValue:label forKey:@"label"];

[NSLayoutConstraint constraintsWithVisualFormat:@"|-[label]-|"
                                   options:0
                                   metrics:nil
                                   // using Obj-C dictionary subscripting syntax
                                   views:@{ @"label" : items[@"label"] }];

Your example may be contrived, but in case it is not, the fact that UILabel *label is in scope means you could just pass NSDictionaryOfVariableBindings(label) for the views: parameter.

Edit: your items mutable dictionary is already correctly set up to just pass in as the views: parameter, if you want.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top