5

In our code in a lot of places, I keep seeing this...

containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":childView]))
containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":childView]))

It just seems redundant to me. I'm wondering if there's a way to combine the formats into a single string; something like this...

containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[view]-0-|;V:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":childView]))

So is something like this possible?

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • Strongly suggest learning / using constraints *without* Visual Format Language. It will be much clearer, and there are many constraints you ***cannot*** set with VFL. – DonMag Apr 05 '18 at 20:03
  • For simple sets, VFL is fine, but for most, agreed. Again, not my code. We're trying to remove redundancies though and I'm seeing this a lot. – Mark A. Donohoe Apr 05 '18 at 20:08

4 Answers4

2

For sorry you can't use this , but you can try something like this

let rr = UIView()

rr.backgroundColor = UIColor.red

self.view.addSubview(rr)

rr.translatesAutoresizingMaskIntoConstraints = false

["H:|-100-[rr]-100-|","V:|-100-[rr]-100-|"].forEach{NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: $0, options: NSLayoutFormatOptions.init(rawValue: 0), metrics: nil, views: ["rr":rr]))}
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • 1
    Brilliant idea! Not the clearest for a noob to look at, but then again, we try not to have noobs on our team! :) Actually, I think I'll create an extension on UIView that takes in a variadic string array so we can do stuff like this easily. Again, really cool idea! – Mark A. Donohoe Apr 05 '18 at 19:49
  • 2
    If you're going to do this, you should be using `forEach` and not `map`. The intention of `map` is to convert one array or sequence into an array. You don't want to create an array here, do you? – vacawama Apr 05 '18 at 21:40
  • In this exceptional case **map** is qual to **forEach** as there is no mapping happens , if you tried to print the result it will be () , actually it's a reduce to a for loop – Shehata Gamal Apr 05 '18 at 21:50
  • 1
    You are creating an array `[(), ()]` and throwing it away. This is a misuse of `map`. – vacawama Apr 05 '18 at 21:58
  • 1
    Good point! ForEach would be better than Map here. @Sh_Khan, can you update your code to use ForEach, and as others have suggested, to activate the constraints instead of adding them? If you do that, I'll mark this as accepted. – Mark A. Donohoe Apr 06 '18 at 15:28
  • I've accepted it, but you should still change it to `NSLayoutConstraint.activate` as recommended by Apple. (See the other post for more info.) – Mark A. Donohoe Apr 06 '18 at 18:24
  • @MarqueIV please uncheck the right answer , i will delete the answer – Shehata Gamal Apr 06 '18 at 19:10
  • Why? Yours is the answer I wanted accepted for using the 'forEach' (originally 'map') which directly addresses my original question of removing redundancy. The other answers don't do that, thus I don't want to accept them. I'm simply saying to change what's inside your closure so you don't manually add them, but rather activate them since that's the correct thing to do. However, that's secondary to my question which is why I accepted yours even with it not activating them. It still addresses my issue. – Mark A. Donohoe Apr 06 '18 at 20:27
1

Comments:

  1. You should be activating constraints instead of adding them to views (since iOS 8).
  2. You can skip the entire options: NSLayoutFormatOptions(rawValue: 0) since that is the default value.
  3. The VFL returns an array of constraints, so you can just add the arrays together.

With those changes we get this line of code:

NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[view]-0-|", metrics: nil, views: ["view":childView]) + NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[view]-0-|", metrics: nil, views: ["view":childView]))
vacawama
  • 150,663
  • 30
  • 266
  • 294
1

Riffing off of Shehata's answer, with different code formatting:

NSLayoutConstraint.activate(
    [
        "H:|-0-[view]-0-|",
        "V:|-0-[view]-0-|"
    ].flatMap {
        NSLayoutConstraint.constraints(
            withVisualFormat: $0,
            metrics: nil,
            views: ["view": gutterView]
        )
    }
)
Cykelero
  • 199
  • 1
  • 9
0

No, unfortunately the syntax doesn't permit this - see the grammar in the docs

Specifically, the line <orientation> H|V means that the value of orientation can be H or V but not both.

A good alternative for programmatic autolayout is to use an open source DSL library - two popular examples of which are Cartography and Snapkit

I've used both and found them much less fiddly than VFL, and much less verbose than the underlying Apple API

Rich Tolley
  • 3,812
  • 1
  • 30
  • 39