NSDictionaryOfVariableBindings быстрый эквивалент?
Документация Apple показывает неурегулированное пустое пространство в разделе "Создание словаря" ссылки UIKit здесь.
Кто-нибудь нашел замену макроса NSDictionaryOfVariableBindings
, или мы должны просто написать наш собственный?
EDIT. Согласно this, возможно, правильный подход - написать глобальную функцию для обработки этого? Похоже, сложные макросы полностью исключены.
Ответы
Ответ 1
Согласно исходному коду Apple:
NSDictionaryOfVariableBindings (v1, v2, v3) эквивалентен [NSDictionary dictionaryWithObjectsAndKeys: v1, @ "v1", v2, @ "v2", v3, @ "v3", nil];
Итак, в Swift вы можете сделать то же самое:
let bindings = ["v1": v1, "v2": v2, "v3": v3]
Ответ 2
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. Это просто, что вы не беспокоились, потому что макрос уже существовал!
Ответ 3
Эта функциональность основана на макрорасширении, которое в настоящее время не поддерживается в Swift.
Я не думаю, что есть какой-либо способ сделать что-то подобное в Swift на данный момент. Я считаю, что вы не можете написать свою собственную замену.
Я боюсь, вам придется вручную развернуть определение словаря, даже если это означает повторение каждого имени дважды.
Ответ 4
Среда выполнения ObjC на помощь!
i создал альтернативное решение, но оно работает только в том случае, если каждое из представлений является переменными экземпляра одного и того же объекта.
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))
}
}
к сожалению, вам все равно придется вводить имя переменной в виде строки, но она, по крайней мере, будет утверждать, если есть опечатка. это определенно не будет работать во всех ситуациях, но, тем не менее, полезно
Ответ 5
Итак, я взломал что-то вместе, что, кажется, работает:
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
Как только вы сохранили все свои представления в качестве свойств, вы также можете использовать отражение так:
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
}
}
Ответ 7
Основанное на fooobar.com/info/93203/... cherpak-evgeny, это расширение 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 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.
Большое спасибо Черпаку Евгению за то, что он написал его.