NSDictionaryOfVariableBindings swift等价?

时间:2014-06-06 15:09:32

标签: ios swift

Apple文档显示了“创建词典”下的一个令人不安的空白区域。 UIKit参考文献here的一部分。

有没有人找到NSDictionaryOfVariableBindings宏的替代品,或者我们是否只想写自己的宏?

编辑 - 根据this,也许正确的方法是编写一个全局函数来处理这个问题?看起来复杂的宏完全没有了。

7 个答案:

答案 0 :(得分:59)

根据Apple源代码:

  

NSDictionaryOfVariableBindings(v1,v2,v3)相当于[NSDictionary dictionaryWithObjectsAndKeys:v1,@" v1",v2,@" v2",v3,@" v3& #34;,nil];

所以在Swift中你可以使用:

let bindings = ["v1": v1, "v2": v2, "v3": v3]

答案 1 :(得分:28)

正如你所说,

NSDictionaryOfVariableBindings是一个宏。 Swift中没有宏。这么多。

尽管如此,您可以轻松编写Swift函数,为字符串中的视图指定字符串名称,然后将该字典传递到constraintsWithVisualFormat。不同之处在于,与Objective-C不同,Swift无法为这些视图看到您的名称;你必须让组成一些新的名称。

[要明确的是,您的Objective-C 代码不能看到您的变量名称;就是说,在宏观评估时,预处理器在源代码上作为文本进行操作并重写它 - 所以它可以只在引号内使用变量名的文本(制作字符串)和在外面(制造价值)来形成字典。但是使用Swift,没有预处理器。]

所以,这就是我的工作:

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
}

你打电话给它并像这样使用它:

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

问题在于由您决定您的可视格式字符串中myOtherView的名称将为v2(因为它在列表中排名第二)传入dictionaryOfNames())。但我可以忍受这一点,只是为了避免每次手工输入字典的乏味。

当然,您也可以在Objective-C中或多或少地编写相同的功能。只是因为宏已经存在而你没有打扰!

答案 2 :(得分:2)

该功能基于宏扩展,目前Swift不支持。

我认为目前在Swift中没有办法做类似的事情。我相信你不能写自己的替代品。

我担心你必须手动展开字典定义,即使这意味着重复每个名字两次。

答案 3 :(得分:1)

ObjC运行时救援!

我创建了一个替代解决方案,但只有在每个视图都是同一个对象的实例变量时它才有效。

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
}

可以像这样使用:

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))

    }

}

不幸的是,您仍然必须以字符串形式输入变量名称,但如果存在拼写错误,它至少会断言。这绝对不会在所有情况下都能奏效,但仍然有用

答案 4 :(得分:0)

将所有视图存储为属性后,您还可以使用如下反射:

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
    }
}

答案 5 :(得分:0)

所以我一起砍了一些似乎可行的东西:

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
}

用法:

        let views = dictionaryOfVariableBindings(container: self, views: imageView)

答案 6 :(得分:0)

基于cherpak-evgeny的https://stackoverflow.com/a/55086673/1058199,此UIViewController扩展假定容器为self,即当前的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's 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
    }
}

在您的viewController中像这样使用它:

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

在Xcode 10.2.1和Swift 4.2中进行了测试。

非常感谢Cherpak Evgeny首先编写它。