Please see the following code:
def viewDidLoad
super
self.view.translatesAutoresizingMaskIntoConstraints = false
self.view.backgroundColor = UIColor.whiteColor
@start = UIButton.buttonWithType(UIButtonTypeRoundedRect).tap do |el|
el.translatesAutoresizingMaskIntoConstraints = false
el.setTitle('Start', forState:UIControlStateNormal)
el.addTarget(self, action:'toStartController', forControlEvents:UIControlEventTouchUpInside)
self.view.addSubview(el)
end
self.layout_subviews
end
def layout_subviews
metrics = { 'space' => 8 }
views_dict = {
'superview' => self.view,
'start' => @start
}
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('H:|-[start(100)]-|',
options: NSLayoutFormatAlignAllCenterX,
metrics: metrics,
views: views_dict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('V:[start]-|',
options: NSLayoutFormatAlignAllBottom,
metrics: metrics,
views: views_dict))
end
The problem I'm having is H:|-[start(100)]-|
won't work. What I want is a button with a width of 100 centered on the X-axis and attached to the bottom of the screen with a default margin. As soon as I remove the (100)
this works, however the button stretches to the width of the screen minus the default margins. When I specify a custom width I reckon the Auto Layout system can't work out what the left and right margins should be. I get an Unable to simultaneously satisfy constraints.
error. I guess this has something to do with the dashes in H:|-[start(100)]-|
, which need to have a fluid width to attach the start
element to the superview
, instead of being the default margins.
Any thoughts on how I might solve this?
Update (thanks ghettopia):
This works (notice I'm using a UINavigationController
in the app delegate and self.view.translatesAutoresizingMaskIntoConstraints = false
in viewDidLoad
of TestController
is commented out):
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds).tap do |win|
controller = TestController.alloc.initWithNibName(nil, bundle:nil)
win.rootViewController = UINavigationController.alloc.initWithRootViewController(controller).tap do |root|
root.navigationBarHidden = true
root.wantsFullScreenLayout = true
end
win.makeKeyAndVisible
end
true
end
end
class TestController < UIViewController
def viewDidLoad
super
# self.view.translatesAutoresizingMaskIntoConstraints = false
self.view.backgroundColor = UIColor.blueColor
@button = UIButton.buttonWithType(UIButtonTypeRoundedRect)
@button.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(@button)
views = {
'view' => self.view,
'button' => @button
}
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('H:[button(100)]', options: 0, metrics: nil, views: views))
self.view.addConstraint(NSLayoutConstraint.constraintWithItem(@button, attribute: NSLayoutAttributeCenterX, relatedBy: NSLayoutRelationEqual, toItem: self.view, attribute: NSLayoutAttributeCenterX, multiplier: 1, constant: 0))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('V:[button]-|', options: 0, metrics: nil, views: views))
end
end
Great. Now I get a blue screen with a button centered on the X-axis with a default margin to the bottom, just as I intended. However, from what I've read self.view.translatesAutoresizingMaskIntoConstraints = false
is required to make Auto Layout work. To make that happen, this works as well (notice I'm not using a UINavigationController
this time and self.view.translatesAutoresizingMaskIntoConstraints = false
in viewDidLoad
of TestController
IS being used):
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds).tap do |win|
win.rootViewController = TestController.alloc.initWithNibName(nil, bundle:nil)
win.makeKeyAndVisible
end
true
end
end
class TestController < UIViewController
def viewDidLoad
super
self.view.translatesAutoresizingMaskIntoConstraints = false
self.view.backgroundColor = UIColor.blueColor
@button = UIButton.buttonWithType(UIButtonTypeRoundedRect)
@button.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(@button)
views = {
'view' => self.view,
'button' => @button
}
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('H:[button(100)]', options: 0, metrics: nil, views: views))
self.view.addConstraint(NSLayoutConstraint.constraintWithItem(@button, attribute: NSLayoutAttributeCenterX, relatedBy: NSLayoutRelationEqual, toItem: self.view, attribute: NSLayoutAttributeCenterX, multiplier: 1, constant: 0))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('V:[button]-|', options: 0, metrics: nil, views: views))
end
end
But that doesn't seem like a proper solution. I need a UINavigationController
to create my app structure. The thing is when I don't use any of the above solutions I get a black screen with a button dead in the middle (on both axis). The views seems to be broken:
Thoughts? Should I just remove self.view.translatesAutoresizingMaskIntoConstraints = false
and forget about it or is it indeed required? If it is, there must be something wrong with my code.
Update 2:
Interesting tutorial: Auto Layout in iOS 6: Adding constraints through code. The author doesn't use self.view.translatesAutoresizingMaskIntoConstraints = false
in viewDidLoad
, only on subviews.
Update 3:
I think I may have solved the black screen problem by moving self.view.translatesAutoresizingMaskIntoConstraints = false
to the init
method instead of viewDidLoad
. Not sure why this works though, but the screen looks identical to the screenshot now, just as I intented in the first place. Here's the updated code:
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds).tap do |win|
controller = TestController.alloc.initWithNibName(nil, bundle:nil)
win.rootViewController = UINavigationController.alloc.initWithRootViewController(controller).tap do |root|
root.navigationBarHidden = true
root.wantsFullScreenLayout = true
end
win.makeKeyAndVisible
end
true
end
end
class TestController < UIViewController
def init
self.view.translatesAutoresizingMaskIntoConstraints = false
end
def viewDidLoad
super
self.view.backgroundColor = UIColor.blueColor
@button = UIButton.buttonWithType(UIButtonTypeRoundedRect)
@button.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(@button)
views = {
'view' => self.view,
'button' => @button
}
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('H:[button(100)]', options: 0, metrics: nil, views: views))
self.view.addConstraint(NSLayoutConstraint.constraintWithItem(@button, attribute: NSLayoutAttributeCenterX, relatedBy: NSLayoutRelationEqual, toItem: self.view, attribute: NSLayoutAttributeCenterX, multiplier: 1, constant: 0))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat('V:[button]-|', options: 0, metrics: nil, views: views))
end
end
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect] ;
button.translatesAutoresizingMaskIntoConstraints = NO ;
[self.view addSubview:button] ;
NSDictionary* views = @{ @"view" : self.view , @"button" : button } ;
// Make button's width 100.
[self.view addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:[button(100)]" options:0 metrics:nil views:views ] ] ;
// Make button's CenterX the same as self.view's CenterX.
[self.view addConstraint: [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0 ] ] ;
// Make button's NSLayoutAttributeBottom the default space away from self.view's NSLayoutAttributeBottom.
[self.view addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:[button]-|" options:0 metrics:nil views:views ] ] ;