NSDictionaryOfVariableBindings fast equivalent?

Apple's documentation shows unsettled white space in the Create Dictionary section of the UIKit link here .

Has anyone found a macro replacement for NSDictionaryOfVariableBindings , or should we just write our own?

EDIT. According to this , perhaps the right approach is to write a global function to handle this? Complex macros seem to be completely ruled out.

+60
ios swift
Jun 06 '14 at 15:09
source share
7 answers

According to Apple source code:

NSDictionaryOfVariableBindings (v1, v2, v3) is equivalent to [NSDictionary dictionaryWithObjectsAndKeys: v1, @ "v1", v2, @ "v2", v3, @ "v3", nil];

So, in Swift, you can do the same:

 let bindings = ["v1": v1, "v2": v2, "v3": v3] 
+59
Jun 10 '14 at 8:03
source share

NSDictionaryOfVariableBindings is, as you say, a macro. There are no macros in Swift. So much for this.

However, you can easily write a Swift function to assign line names to your views in a dictionary, and then pass that dictionary to constraintsWithVisualFormat . The difference is that, unlike Objective-C, Swift cannot see your names for these views; you will have to allow him to create new names.

[To be clear, this does not mean that your Objective-C code could see your variable names; this is that during the macro evaluation, the preprocessor worked on your source code as text and rewrote it - and so it could just use the text of your variable names both inside quotation marks (to create strings) and outside (to create values) to form a dictionary. But with Swift there is no preprocessor.]

So here is what I am doing:

 func dictionaryOfNames(arr:UIView...) -> Dictionary<String,UIView> { var d = Dictionary<String,UIView>() for (ix,v) in arr.enumerate(){ d["v\(ix+1)"] = v } return d } 

And you name it and use it like this:

  let d = dictionaryOfNames(myView, myOtherView, myFantasicView) myView.addConstraints( NSLayoutConstraint.constraintsWithVisualFormat( "H:|[v2]|", options: nil, metrics: nil, views: d) ) 

The trick is that it is up to you to decide that the name for myOtherView in your visual format string will be v2 (since it was second in the list passed to dictionaryOfNames() ). But I can live with it simply to avoid the tedious manual printing of the dictionary every time.

Of course, you could equally well write more or less the same function in Objective-C. It’s just that you weren’t worried because the macro already existed!

+28
Jul 06 '14 at 1:48
source share

This functionality is based on macro expansion, which is currently not supported in Swift.

I don't think there is any way to do something similar in Swift at the moment. I believe that you cannot write your own replacement.

I'm afraid you will have to manually expand the dictionary definition, even if it means repeating each name twice.

+2
Jun 06 '14 at 15:20
source share

ObjC runtime to the rescue!

i created an alternative solution, but it only works if each of the views is instance variables of the same object.

 func DictionaryOfInstanceVariables(container:AnyObject, objects: String ...) -> [String:AnyObject] { var views = [String:AnyObject]() for objectName in objects { guard let object = object_getIvar(container, class_getInstanceVariable(container.dynamicType, objectName)) else { assertionFailure("\(objectName) is not an ivar of: \(container)"); continue } views[objectName] = object } return views } 

can be used as follows:

 class ViewController: UIViewController { var childA: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = UIColor.redColor() return view }() var childB: UIButton = { let view = UIButton() view.setTitle("asdf", forState: .Normal) view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = UIColor.blueColor() return view }() override func viewDidLoad() { super.viewDidLoad() self.view.addSubview(childA) self.view.addSubview(childB) let views = DictionaryOfInstanceVariables(self, objects: "childA", "childB") self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[childA]|", options: [], metrics: nil, views: views)) self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[childB]|", options: [], metrics: nil, views: views)) self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[childA][childB(==childA)]|", options: [], metrics: nil, views: views)) } } 

unfortunately, you still have to enter the variable name as a string, but it will at least claim if there is a typo. this will definitely not work in all situations, but nonetheless useful

+1
Jan 29 '16 at 23:59
source share

So, I hacked something together, which seems to work:

 func dictionaryOfVariableBindings(container: Any, views:UIView...) -> Dictionary<String, UIView> { var d = Dictionary<String, UIView>() let mirror = Mirror(reflecting: container) let _ = mirror.children.compactMap { guard let name = $0.label, let view = $0.value as? UIView else { return } guard views.contains(view) else { return } d[name] = view } return d } 

Using:

  let views = dictionaryOfVariableBindings(container: self, views: imageView) 
+1
Mar 10 '19 at 10:24
source share

Once you have saved all your views as properties, you can also use reflection like this:

 extension ViewController { func views() -> Dictionary<String, AnyObject> { var views = dictionaryOfProperties() views.forEach { if !($1 is UIView) { views[$0] = nil } } return views } } extension NSObject { func dictionaryOfProperties() -> Dictionary<String, AnyObject> { var result = Dictionary<String, AnyObject>() let mirror = Mirror(reflecting: self) for case let(label?, value) in mirror.children { result[label] = value as? AnyObject } return result } } 
0
Apr 01 '16 at 16:22
source share

Based on https://stackoverflow.com/a/3203/... cherpak-evgeny, this UIViewController extension assumes the container is self , the current instance of viewController.

 extension UIViewController { // Alex Zavatone 06/04/2019 // Using reflection, get the string name of the UIView properties passed in // to create a dictionary of ["viewPropertyName": viewPropertyObject…] like // Objective-C NSDictionaryForVariableBindings. func dictionaryOfBindings(_ arrayOfViews:[UIView?]) -> Dictionary<String, UIView> { var bindings = Dictionary<String, UIView>() let viewMirror = Mirror(reflecting: self) let _ = viewMirror.children.compactMap { guard let name = $0.label, let view = $0.value as? UIView else { return } guard arrayOfViews.contains(view) else { return } bindings[name] = view } return bindings } } 

Use it like this in your viewController:

 let viewArray = [mySwitch, myField, mySpinner, aStepper, someView] let constraintsDictionary = dictionaryOfBindings(viewArray) 

Tested in Xcode 10.2.1 and Swift 4.2.

Many thanks to Eugene Cherpak for writing it.

0
Jun 04 '19 at 16:45
source share



All Articles