web-dev-qa-db-fra.com

Utilisation du format visuel Autolayout avec Swift?

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!.

33
Mike Walker

cela fonctionne pour moi sans erreur:

let bar:[AnyObject]! = NSLayoutConstraint.constraintsWithVisualFormat(
  nil, options: NSLayoutFormatOptions(0), metrics: nil, views: nil)

mise à jour

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

mise à jour

(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!

67
holex

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))
13
John M. P. Knox

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)
5
Nate Cook

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)
    }
5
StrangeDays

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

  • Cartographie , qui est une nouvelle approche, mais assez impressionnante. Il utilise Autolayout sous le capot.
  • SnapKit , que je n'ai pas essayé mais qui est aussi un framework de mise en page automatique DSL
4
Dan Rosenstark

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: [:])
3
juanjo

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]-|")
1
milos

Vous devez accéder à la structure NSLayoutFormatOptions.

Les travaux suivants pour moi.

self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("",
options:NSLayoutFormatOptions.AlignAllBaseline,
metrics: nil, views: nil))
0
Borja Igartua
// 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)
0
Michael Peterson