J'ai essayé d'utiliser le Autolayout Visual Format Language dans Swift , en utilisant NSLayoutConstraint.constraintsWithVisualFormat
. Voici un exemple de code qui ne fait rien d'utile, mais pour autant que je sache, devrait rendre le vérificateur de type heureux:
let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
format: "", options: 0, metrics: {}, views: {})
Cependant, cela déclenche l'erreur du compilateur:
"Impossible de convertir le type d'expression" [AnyObject]! " pour taper 'String!' ".
Avant de supposer qu'il s'agit d'un bug digne d'un radar, y a-t-il quelque chose d'évident qui me manque ici? Cela se produit même sans le transtypage explicite du nom de la variable, ou avec d'autres transtypages gratuits utilisant as
. Je ne vois aucune raison pour laquelle le compilateur s'attendrait à ce qu'une partie de ce problème soit résolue en String!
.
cela fonctionne pour moi sans erreur:
let bar:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
nil, options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
la ligne ci-dessus peut ne pas être compilée depuis le 1st et 4e les paramètres ne peuvent plus être optionnels.
syntaxiquement ceux-ci doivent être définis, comme par exemple cette:
let bar:[AnyObject] = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: ["": self.view])
(pour Xcode 7, Swift 2.0)
la syntaxe valide demande également le nom des paramètres, comme:
NSLayoutFormatOptions(rawValue: 0)
REMARQUE: cette ligne de code affiche uniquement la syntaxe correcte, les paramètres eux-mêmes ne garantissent pas que la contrainte sera correcte ou même valide!
Le premier problème ici est que Swift Dictionary n'est pas encore ponté avec NSDictionary. Pour que cela fonctionne, vous voudrez créer explicitement un NSDictionary pour chaque paramètre de type NSDictionary.
En outre, comme le souligne Spencer Hall, {} n'est pas un dictionnaire littéral dans Swift. Le dictionnaire vide s'écrit:
[:]
Depuis XCode 6 Beta 2, cette solution vous permet de créer des contraintes au format visuel:
var viewBindingsDict: NSMutableDictionary = NSMutableDictionary()
viewBindingsDict.setValue(fooView, forKey: "fooView")
viewBindingsDict.setValue(barView, forKey: "barView")
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[fooView]-[barView]-|", options: nil, metrics: nil, views: viewBindingsDict))
Essayez ceci - supprimez le nom de variable initial (format:
), Utilisez NSLayoutFormatOptions(0)
, et passez simplement nil
pour ces NSDictionaries facultatifs:
let foo:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat("", options: NSLayoutFormatOptions(0), metrics: nil, views: nil)
Pour info: si vous utilisez des vues avec constraintWithVisualFormat - au lieu d'envelopper avec NSMutableDict
["myLabel": self.myLabel!]
et pour être plus précis
var constraints = [NSLayoutConstraint]()
NSLayoutConstraint.constraintsWithVisualFormat("H:|-15-[myLabel]-15-|",
options:NSLayoutFormatOptions.allZeros,
metrics: nil,
views: ["myLabel": self.myLabel!]).map {
constraints.append($0 as NSLayoutConstraint)
}
Cela fonctionne avec Xcode 6.1.1:
extension NSView {
func addConstraints(constraintsVFL: String, views: [String: NSView], metrics: [NSObject: AnyObject]? = nil, options: NSLayoutFormatOptions = NSLayoutFormatOptions.allZeros) {
let mutableDict = (views as NSDictionary).mutableCopy() as NSMutableDictionary
let constraints = NSLayoutConstraint.constraintsWithVisualFormat(constraintsVFL, options: options, metrics: metrics, views: mutableDict)
self.addConstraints(constraints)
}
}
Ensuite, vous pouvez utiliser des appels comme:
var views : [String: NSView] = ["box": self.box]
self.view.addConstraints("V:[box(100)]", views: views)
Cela fonctionne pour ajouter des contraintes. Si vous utilisez iOS, remplacez UIView
par NSView
Vous devriez probablement vérifier
NSLayoutFormatOptions
implémente le protocole OptionSetType
, qui hérite de SetAlgebraType
qui hérite de ArrayLiteralConvertible
, vous pouvez donc initialiser NSLayoutFormatOptions
comme ceci: []
ou ca: [.DirectionLeftToRight, .AlignAllTop]
Ainsi, vous pouvez créer les contraintes de mise en page comme ceci:
NSLayoutConstraint.constraintsWithVisualFormat("", options: [], metrics: nil, views: [:])
Cela m'énerve un peu d'appeler NSLayoutConstraint
(singulier) pour générer constraintsWithVisualFormat...
(Pluriel), même si je suis sûr que c'est juste moi. Dans tous les cas, j'ai ces deux fonctions de haut niveau:
extrait 1 (Swift 1.2)
#if os(iOS)
public typealias View = UIView
#elseif os(OSX)
public typealias View = NSView
#endif
public func NSLayoutConstraints(visualFormat: String, options: NSLayoutFormatOptions = .allZeros, views: View...) -> [NSLayoutConstraint] {
return NSLayoutConstraints(visualFormat, options: options, views: views)
}
public func NSLayoutConstraints(visualFormat: String, options: NSLayoutFormatOptions = .allZeros, views: [View] = []) -> [NSLayoutConstraint] {
if visualFormat.hasPrefix("B:") {
let h = NSLayoutConstraints("H\(dropFirst(visualFormat))", options: options, views: views)
let v = NSLayoutConstraints("V\(dropFirst(visualFormat))", options: options, views: views)
return h + v
}
var dict: [String:View] = [:]
for (i, v) in enumerate(views) {
dict["v\(i + 1)"] = v
}
let format = visualFormat.stringByReplacingOccurrencesOfString("[v]", withString: "[v1]")
return NSLayoutConstraint.constraintsWithVisualFormat(format, options: options, metrics: nil, views: dict) as! [NSLayoutConstraint]
}
Qui peut être utilisé comme ceci:
superView.addConstraints(NSLayoutConstraints("B:|[v]|", view))
En d'autres termes, les vues sont nommées automatiquement "v1"
En "v\(views.count)"
(sauf la première vue qui peut également être appelée "v"
). De plus, le préfixe du format avec "B:"
Générera les contraintes "H:"
Et "V:"
. L'exemple de ligne de code ci-dessus signifie donc "assurez-vous que le view
correspond toujours au superView
".
Et avec les extensions suivantes:
extrait 2
public extension View {
// useMask of nil will not affect the views' translatesAutoresizingMaskIntoConstraints
public func addConstraints(visualFormat: String, options: NSLayoutFormatOptions = .allZeros, useMask: Bool? = false, views: View...) {
if let useMask = useMask {
for view in views {
#if os(iOS)
view.setTranslatesAutoresizingMaskIntoConstraints(useMask)
#elseif os(OSX)
view.translatesAutoresizingMaskIntoConstraints = useMask
#endif
}
}
addConstraints(NSLayoutConstraints(visualFormat, options: options, views: views))
}
public func addSubview(view: View, constraints: String, options: NSLayoutFormatOptions = .allZeros, useMask: Bool? = false) {
addSubview(view)
addConstraints(constraints, options: options, useMask: useMask, views: view)
}
}
Nous pouvons effectuer certaines tâches courantes de manière beaucoup plus élégante, comme ajouter un bouton avec un décalage standard dans le coin inférieur droit:
superView.addSubview(button, constraints: "B:[v]-|")
Par exemple, dans une aire de jeux iOS:
import UIKit
import XCPlayground
// paste here `snippet 1` and `snippet 2`
let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
XCPShowView("view", view)
view.backgroundColor = .orangeColor()
XCPShowView("view", view)
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.setTitle("bottom right", forState: .Normal)
view.addSubview(button, constraints: "B:[v]-|")
Vous devez accéder à la structure NSLayoutFormatOptions
.
Les travaux suivants pour moi.
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
options:NSLayoutFormatOptions.AlignAllBaseline,
metrics: nil, views: nil))
// topLayoutGuide constraint
var views: NSMutableDictionary = NSMutableDictionary()
views.setValue(taskNameField, forKey: "taskNameField")
views.setValue(self.topLayoutGuide, forKey: "topLayoutGuide")
let verticalConstraint = "V:[topLayoutGuide]-20-[taskNameField]"
let constraints:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(verticalConstraint, options: NSLayoutFormatOptions(0), metrics: nil, views: views)
self.view.addConstraints(constraints)
// bottomLayoutGuide constraint
var views: NSMutableDictionary = NSMutableDictionary()
views.setValue(logoutButton, forKey: "logoutButton")
views.setValue(self.bottomLayoutGuide, forKey: "bottomLayoutGuide")
let verticalConstraint = "V:[logoutButton]-20-[bottomLayoutGuide]"
let constraints:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(verticalConstraint, options: NSLayoutFormatOptions(0), metrics: nil, views: views)
self.view.addConstraints(constraints)