I am having a Subview(say MainView) of NSView, with NSTextField inside it.
Note: This code is based on this github.repo
MainView has designated initializer as follows
init(title : String, layout :NSLayoutAttribute) {
innerView = self.createTextField(title)//Text field with stringValue = title
mainView.addSubview(innerView)
createViewConstrants(innerView, layout: layout)
}
layout can be Left
, Right
or CenterX
.
(Inner TextView will be vertically centered always, only Horizontal alignment can vary)
Following function makes constraint for the purpose
func createViewConstrants(view : NSView, layout :NSLayoutAttribute) {
let viewMap = ["innerView" : view, "superview" : mainView]
var constraints :[AnyObject]!
switch layout {
case .Left:
println("Left")
constraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:[superview]-(<=1)-[innerView]",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: viewMap)
mainView.addConstraints(constraints)
case .Right:
println("Right")
constraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:[superview]-(<=1)-[innerView]-|",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: viewMap)
mainView.addConstraints(constraints)
case .CenterX:
println("Center")
constraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:[superview]-(<=1)-[innerView]",
options: NSLayoutFormatOptions.AlignAllCenterX,
metrics: nil,
views: viewMap)
mainView.addConstraints(constraints)
constraints = NSLayoutConstraint.constraintsWithVisualFormat(
"H:[superview]-(<=1)-[innerView]",
options: NSLayoutFormatOptions.AlignAllCenterY,
metrics: nil,
views: viewMap)
mainView.addConstraints(constraints)
default:
fatalError("Invalid layout")
}
}
Align Right
and Center
are working fine, but Align left
is not working.
Also since the vertically centering is common to all the cases can it be taken out of the switch case ?
Several issues:
You don't normally list the superview explicitly in VFL. Rather, you use the vertical line (|
) character. This:
H:[superview]-(<=1)-[innerView]
doesn't mean that innerView
's leading edge is less than 1 point "after" the superview's leading edge. It means that innerView
's leading edge is less than 1 point after the superview's trailing edge, which could put innerView
outside of its superview's bounds, which would cause it to be clipped.
If these are the only constraints you're adding that affect innerView
, then your layout is ambiguous. An inequality allows any of the possible values and the system has no reliable way of picking one of those values over any other.
You can't really express centering in the VFL. You can pass along an alignment flag to constraintsWithVisualFormat(_:options:metrics:views:)
(as you're aware), but a) that's not really part of the visual format language as such and b) you can't do that without also creating some bogus, unwanted constraints in the other orientation. That is, you can't use the alignment flag to center things vertically unless you also create some bogus horizontal constraints. (And vice versa.) That's because the alignment flags only apply to the views listed in the VFL string and the VFL string always implies constraints.
Why do you insist on using VFL? If you use init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:)
, you can simply pass along the layout
parameter that your method receives. There would be no need for a switch
with multiple cases. You would just do:
var constraint = NSLayoutConstraint(item: innerView,
attribute: layout,
relatedBy: .Equal,
toItem: mainView,
attribute: layout
multiplier: 1,
constant: 0)
mainView.addConstraint(constraint)
constraint = NSLayoutConstraint(item: innerView,
attribute: .CenterY,
relatedBy: .Equal,
toItem: mainView,
attribute: .CenterY
multiplier: 1,
constant: 0)
mainView.addConstraint(constraint)
That would handle all three cases.