Positioning UIButtons at top of Screen - ios

What I want is to have 5 buttons equally spaced out at the top of the screen. I tried using constraints but the buttons would get squished.
I have this code so far:
- (void)viewDidLoad {
[super viewDidLoad];
//I know this is only two buttons
NSUInteger space = (_backround.frame.size.width - _squareOne.frame.size.width * 5) / 6;
CGRect newFrameOne;
newFrameOne = CGRectMake(space, 50, 55, 55);
CGRect newFrameTwo;
newFrameTwo = CGRectMake(space * 2 + _squareOne.frame.size.width, 50, 55, 55);
_squareOne.frame = newFrameOne;
_squareTwo.frame = newFrameTwo;
}
The buttons never show up... and I don't know any other ways to do this...
Any help?

Could you try something like this below?
Update: I've written the code properly using Xcode this time (previously I typed it from memory).
See below screenshot for the final result.
View Controller Header File
#interface ViewController : UIViewController
#property (nonatomic, strong) UIButton *btn1;
#property (nonatomic, strong) UIButton *btn2;
#property (nonatomic, strong) UIButton *btn3;
#property (nonatomic, strong) UIButton *btn4;
#property (nonatomic, strong) UIButton *btn5;
#end
View Controller Implementation File
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initViews];
[self initConstraints];
}
-(void)initViews
{
self.btn1 = [[UIButton alloc] init];
self.btn1.backgroundColor = [UIColor grayColor];
self.btn2 = [[UIButton alloc] init];
self.btn2.backgroundColor = [UIColor grayColor];
self.btn3 = [[UIButton alloc] init];
self.btn3.backgroundColor = [UIColor grayColor];
self.btn4 = [[UIButton alloc] init];
self.btn4.backgroundColor = [UIColor grayColor];
self.btn5 = [[UIButton alloc] init];
self.btn5.backgroundColor = [UIColor grayColor];
[self.view addSubview:self.btn1];
[self.view addSubview:self.btn2];
[self.view addSubview:self.btn3];
[self.view addSubview:self.btn4];
[self.view addSubview:self.btn5];
}
-(void)initConstraints
{
self.btn1.translatesAutoresizingMaskIntoConstraints = NO;
self.btn2.translatesAutoresizingMaskIntoConstraints = NO;
self.btn3.translatesAutoresizingMaskIntoConstraints = NO;
self.btn4.translatesAutoresizingMaskIntoConstraints = NO;
self.btn5.translatesAutoresizingMaskIntoConstraints = NO;
id views = #{
#"btn1": self.btn1,
#"btn2": self.btn2,
#"btn3": self.btn3,
#"btn4": self.btn4,
#"btn5": self.btn5
};
// horizontal constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[btn1]-5-[btn2(==btn1)]-5-[btn3(==btn1)]-5-[btn4(==btn1)]-5-[btn5(==btn1)]-5-|" options:0 metrics:nil views:views]];
// vertical constraints
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.btn1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:5.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.btn2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.btn1 attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.btn3 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.btn1 attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.btn4 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.btn1 attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.btn5 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.btn1 attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
}
Result
This is what I get.
What long black strip are you referring to, the battery indicator?

Related

How to add UIButton on WKWebview?

I have two viewcontrollers which the viewcontroller1 has a NSTimer countdown to 0 and present viewcontroller2.
The viewcontroller2 has a wkwebview, and I also want to add UIButton on it.
Because I want to create a close button to dismiss viewcontroller2.
I try adding the UIButton on wkwebview, but I can't control it's NSlayoutConstrain.
What's wrong with my code?
Thanks.
red button is on right bottom.
// ViewController1.m
-(void)timerFired {
NSLog(#"===) self.count : %d", self.count);
if (self.count == 0) {
[self.timer invalidate];
self.timer = nil;
ViewController2 *vc = [[ViewController2 alloc] init];
[self presentViewController:vc animated:YES completion:nil];
} else {
self.count -= 1;
}
}
// ViewController2.m
#import "ViewController2.h"
#import <WebKit/WebKit.h>
#interface ViewController2 ()<WKScriptMessageHandler, WKNavigationDelegate,WKUIDelegate>
#property (nonatomic,strong) WKWebView* webView;
#property (nonatomic,strong) UIButton* button;
#property (nonatomic, strong) WKWebViewConfiguration * webConfig;
#end
#implementation ViewController2
- (void)viewDidLoad {
[super viewDidLoad];
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:[self createWKWebApp]];
[self.view addSubview: self.webView];
[self.webView.configuration.preferences setValue:#YES forKey:#"allowFileAccessFromFileURLs"];
self.webView.scrollView.bounces = NO;
[self.webView setContentMode:UIViewContentModeScaleAspectFit];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
NSURL *url = [NSURL URLWithString:#"https://stackoverflow.com/"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
self.button = [UIButton buttonWithType: UIButtonTypeCustom];
self.button.translatesAutoresizingMaskIntoConstraints = false;
self.button.backgroundColor = UIColor.redColor;
[self.button setTitle:#"CLOSE" forState:UIControlStateNormal];
[self.button addTarget:self action:#selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.button];
NSLayoutConstraint *btnRight = [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-20.0];
NSLayoutConstraint *btnTop = [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20.0];
NSLayoutConstraint *btnWidth = [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1.0 constant:30.0];
NSLayoutConstraint *btnHeight = [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:30.0];
[self.view addConstraints:#[btnRight, btnTop, btnWidth, btnHeight]];
}
- (void)btnClicked:(UIButton *)sender {
NSLog(#"BTN CLICKED");
[self dismissViewControllerAnimated:false completion:nil];
}
- (WKWebViewConfiguration *)createWKWebApp {
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
WKUserContentController *userContent = [[WKUserContentController alloc] init];
config.userContentController = userContent;
return config;
}
- (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)message {
NSString *name = message.name;
NSLog(#"===) message name = %#",name);
NSLog(#"===) body class = %#",[message.body class]);
}
You should konw the constraint like this
item1.attribute1 = multiplier × item2.attribute2 + constant
So the right constraint constant should be -20
NSLayoutConstraint *btnRight = [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-20.0];
It might help you. Use these methods.
-(void)view.sendSubviewToBack(subview)
-(void)view.bringSubviewToFront(subview)
Usage:
[self.view sendSubviewToBack:webview];
[self.view bringSubviewToFront:button];

IOS/Objective-C: Center Line under Button Programmatically

I am trying to do a simple line under three buttons to create a Tabs effect. While I could do this in Storyboard, I'd like to do it in code and ideally make the line a subview of an element already in the view to avoid having to give it special constraints.
My approach has been to create a UILabel with no text and a background color. I can make the UILabel a subview of the View, however, that does not attach it to the bottom of the buttons.
On the other hand, if I make the UILabel a subview of one of the buttons, I can't see it.
Can anyone suggest a simple way to do this?
[centerButton setTitle:#"Favorites" forState:UIControlStateNormal];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 120, 280, 2)];
label.backgroundColor = [UIColor redColor];
label.textAlignment = NSTextAlignmentCenter;
label.numberOfLines = 1;
label.text = #"";
[self.view addSubview: label];
Thanks in advance for any suggestions.
Edit:
I tried adding some NSLayoutConstraints Programmatically:
NSLayoutConstraint *con3 = [NSLayoutConstraint
constraintWithItem:label attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:centerButton
attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[label addConstraints:#[con3]];
//tried with and without
[self.view layoutIfNeeded];
This returned the exception:
[LayoutConstraints] The view hierarchy is not prepared
for the constraint: <NSLayoutConstraint:0x174e85500 V:
[UIButton:0x100b0baf0'Now']-(0)-[UILabel:0x108555160]
(inactive)>
When added to a view, the constraint's items
must be descendants of that view (or the view itself).
This will crash if the constraint needs to be resolved
before the view hierarchy is assembled.
Break on -[UIView(UIConstraintBasedLayout)
_viewHierarchyUnpreparedForConstraint:] to debug.
The button created in storyboard is a subview of self.view and I add the label to self.view as a subview in code so not sure why the exception is occurring.
I wonder from the error message if you're possibly attempting to add the NSLayoutConstraint prior to the redLabel view being added to it's parentView (or too early in the lifecycle). In any event, I think this is pretty close to what you're attempting to accomplish:
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) UILabel *previousRedLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)buttonClicked:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *clickedButton = (UIButton *)sender;
UIView *buttonSuperview = [clickedButton superview];
if (buttonSuperview != nil) {
[self _putRedLineWithHeight:3.0f atTheBottomOfView:buttonSuperview animate:YES];
}
}
}
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfView:(UIView *)viewToPutUnder animate:(BOOL)animate {
// remove our previous red line
if (self.previousRedLabel) {
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to check if the superView == viewToPutUnder
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
}
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[viewToPutUnder addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, #"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to parentView's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == parentView's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now the bottom constraint (place it at the bottom of the parent view)
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:viewToPutUnder attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[viewToPutUnder addConstraints:#[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate) {
[UIView animateWithDuration:0.6f animations:^{
[redLabel layoutIfNeeded];
}];
}
self.previousRedLabel = redLabel;
}
Example of Animated:
And one of Non-Animated:
EDITED ANSWER TO HANDLE THE CASE IF EACH BUTTON ISN'T IN IT'S OWN SUPERVIEW
Adjusted for if all buttons are in one superview (width based on button width, center to button center, and pinning the top of the label to the bottom of the button)
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) UILabel *previousRedLabel;
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (IBAction)buttonClicked:(id)sender {
if ([sender isKindOfClass:[UIButton class]]) {
UIButton *clickedButton = (UIButton *)sender;
// if you want it to be a no-op here if they click the same button
// you'll need to add some logic to store the previous clicked button and check whether it's the same button
[self _putRedLineWithHeight:3.0f atTheBottomOfButton:clickedButton animate:YES];
}
}
- (void)_putRedLineWithHeight:(CGFloat)height atTheBottomOfButton:(UIButton *)button animate:(BOOL)animate {
UIView *buttonSuperview = button.superview;
NSAssert(buttonSuperview != nil, #"Button has to have a superview");
// remove our previous red line
if (self.previousRedLabel) {
[self.previousRedLabel removeFromSuperview];
self.previousRedLabel = nil;
}
UILabel *redLabel = [[UILabel alloc] init];
// we're using autolayout so we don't want any resizing from it
redLabel.translatesAutoresizingMaskIntoConstraints = NO;
redLabel.backgroundColor = [UIColor redColor];
// start out with alpha = 0
redLabel.alpha = 0.0f;
// add it to our parentView
[buttonSuperview addSubview:redLabel];
// height (determined by passed in value)
NSAssert(height >= 0, #"Height must be a positive number");
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:height];
// width equal to button's width
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0.0f];
// center x == button's center x
NSLayoutConstraint *centerConstraint = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:redLabel attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f];
// now pin the top of the label to the bottom of the button
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:redLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:button attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f];
// add the height constraint to our label
[redLabel addConstraint:heightConstraint];
// and all the other constraints to our parent view
[buttonSuperview addConstraints:#[widthConstraint, centerConstraint, bottomConstraint]];
redLabel.alpha = 1.0f;
if (animate) {
[UIView animateWithDuration:0.6f animations:^{
[redLabel layoutIfNeeded];
}];
}
self.previousRedLabel = redLabel;
}
#end
Animated:
Not Animated:

Change Frame of Subview in Superview

I have a ViewController that adds an UIView, SpeciesImageView as a subview in viewDidLoad and set constraints in viewWillLayoutSubviews.
SpeciesImageView does not have a nib file. When we create speciesImageView in viewDidLoad, it calls initWithFrame in SpeciesImageView class.
This works fine (in both landscape and portrait) until the phone rotates. I tried setting the constraints as speciesImageView.frame.size.width, but that doesn't work because initWithFrame isn't called when the orientation changes, so the height/width of the speciesImageView remains unchanged.
On the other hand, using screenRect doesn't change the actual size of the UIView, it changes its size within the superview. So in other words, I haven't found a way to change the size of the actual speciesImageView on orientation change.
And for reasons lost to me, it gets completely messed up when you rotate it back to the original position.
- (void)viewDidLoad
{
self.tabBarController.tabBar.hidden=YES;
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
self.navigationController.navigationBar.hidden = NO;
//self.navigationController.navigationBar.translucent = YES;
UIImage *plantinfo;
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
plantinfo = [UIImage imageNamed:#"plantinfo_frame.png"];
} else {
plantinfo = [UIImage imageNamed:#"plantinfo.png"];
}
UIBarButtonItem *tempButton = [[UIBarButtonItem alloc] initWithImage:plantinfo
style:UIBarButtonItemStylePlain
target:self
action:#selector(toggleText:)];
self.navigationItem.rightBarButtonItem = tempButton;
[tempButton release];
self.title = theSpecies.scientificName;
//[self.navigationItem.backBarButtonItem setTitle:#""];
self.navigationItem.backBarButtonItem.title = #"";
infoViewSegmentedControl.backgroundColor = [UIColor blackColor];
webView.backgroundColor = [UIColor blackColor];
_activityIndicator.hidden = YES;
[webView setOpaque:YES];
webView.delegate = self;
// Do double justification
[webView loadHTMLString:[self formatHTML:theSpecies] baseURL:nil];
showingInfoView = NO;
//
// Resize containerView, infoview according to iphone 5 screen size.
//
infoView.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
CGPoint screenOrigin = [[UIScreen mainScreen] bounds].origin;
CGSize viewSize = [[UIScreen mainScreen] bounds].size;
CGPoint origin = infoView.frame.origin;
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
infoView.frame = CGRectMake(screenOrigin.x,
screenOrigin.y + statusBarFrame.size.height,
viewSize.width,
viewSize.height - origin.y - statusBarFrame.size.height);
speciesImageView = [[SpeciesImageView alloc]
initWithFrame:CGRectMake(screenOrigin.x,
screenOrigin.y,
viewSize.width,
viewSize.height)];
} else {
infoView.frame = CGRectMake(screenOrigin.x,
screenOrigin.y,
viewSize.width,
viewSize.height - origin.y - statusBarFrame.size.height);
speciesImageView = [[SpeciesImageView alloc]
initWithFrame:CGRectMake(screenOrigin.x,
screenOrigin.y,
viewSize.width,
viewSize.height - statusBarFrame.size.height)];
}
speciesImageView.delegate = self;
[containerView addSubview:speciesImageView];
managedObjectContext = [(LeafletAppDelegate*)[[UIApplication sharedApplication] delegate] managedObjectContext];
[self parseImageURLArray];
}
-(void)viewWillLayoutSubviews{
if(speciesImageView.window != nil){
CGRect screenRect = [[UIScreen mainScreen] bounds];
speciesImageView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *widthConst = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:screenRect.size.width];
NSLayoutConstraint *heightConst = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:screenRect.size.height];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint
constraintWithItem:speciesImageView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self.view addConstraints:#[widthConst, heightConst, bottomConstraint, rightConstraint]];
}
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
imageScrollView = [[UIScrollView alloc] initWithFrame:frame];
imageScrollView.delegate = self;
imageScrollView.backgroundColor = [UIColor blackColor];
[self addSubview:imageScrollView];
imageScrollView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *widthConst = [NSLayoutConstraint constraintWithItem:imageScrollView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:imageScrollView.frame.size.width];
NSLayoutConstraint *heightConst = [NSLayoutConstraint constraintWithItem:imageScrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:imageScrollView.frame.size.height];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint
constraintWithItem:imageScrollView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint
constraintWithItem:imageScrollView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
[self addConstraints:#[widthConst, heightConst, bottomConstraint, rightConstraint]];
}
return self;
}
If you want your views to adjust to the size of the superview,
then you need something like this (set the margin to whatever you like):
CGFloat margin = 0;
NSString * visualFormatH = [NSString stringWithFormat:#"|-(%f)-[speciesImageView]-(%f)-|", margin, margin];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatH
options:0
metrics:Nil
views:#{#"speciesImageView": speciesImageView}]];
NSString * visualFormatV = [NSString stringWithFormat:#"V:|-(%f)-[speciesImageView]-(%f)-|", margin, margin];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatV
options:0
metrics:Nil
views:#{#"speciesImageView": speciesImageView}]];
Now speciesImageView will adjust its frame whenever the superview frame changes.
Here is a generic example:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView * sampleView = [UIView new];
sampleView.backgroundColor = [UIColor redColor];
sampleView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:sampleView];
CGFloat margin = 0;
NSString * visualFormatH = [NSString stringWithFormat:#"|-(%f)-[sampleView]-(%f)-|", margin, margin];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatH
options:0
metrics:Nil
views:#{#"sampleView": sampleView}]];
NSString * visualFormatV = [NSString stringWithFormat:#"V:|-(%f)-[sampleView]-(%f)-|", margin, margin];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatV
options:0
metrics:Nil
views:#{#"sampleView": sampleView}]];
}
Auto Layout Getting Started
Auto Layout Visual Format Documentation

Custom UIView Subclass background color not being set with Autolayout enabled

I am creating a subclass of UIView with AutoLayout and cannot get it's background color to change for the life of me. Can someone take a look at the simple code and verify I am doing everything that needs to be done in the subclass of the UIView(PCCGenericRatingView). I read on SO that setting the background color only works if the frame is set. Shouldn't it be setting my frame to something other than CGRectZero after evaluating the constraints? I am printing out the view's frame in the viewDidAppear: lifecycle method, but it appears to be CGRectZero.
Edit:
When the frame is passed in to the views initWithFrame initializer, the background color is shown. Otherwise, it is now shown.
PCCGenericRatingView.h file:
#import <UIKit/UIKit.h>
#interface PCCGenericRatingView : UIView
#property (nonatomic, strong) UILabel *titleLabel;
#property (nonatomic, strong) UILabel *messageLabel;
- (instancetype)initWithTitle:(NSString *)title andMessage:(NSString *)message;
+ (instancetype)genericRatingViewWithTitle:(NSString *)title andMessage:(NSString *)message;
#end
PCCGenericRatingView.m file:
#import "PCCGenericRatingView.h"
#implementation PCCGenericRatingView
- (instancetype)initWithTitle:(NSString *)title andMessage:(NSString *)message {
if (self = [super init]) {
self.titleLabel = [[UILabel alloc] init];
self.messageLabel = [[UILabel alloc] init];
_titleLabel.text = title;
_messageLabel.text = message;
[self customizeView];
}
return self;
}
- (void)customizeView {
[self addSubview:_titleLabel];
[self addSubview:_messageLabel];
[_titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_messageLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_titleLabel setBackgroundColor:[UIColor yellowColor]];
[_messageLabel setBackgroundColor:[UIColor yellowColor]];
[_messageLabel setNumberOfLines:0];
[_messageLabel setTextAlignment:NSTextAlignmentCenter];
[_titleLabel setTextAlignment:NSTextAlignmentCenter];
[_titleLabel setFont:[UIFont fontWithName:#"HelveticaNeue-Bold" size:24.0f]];
[_messageLabel setFont:[UIFont fontWithName:#"HelveticaNeue-Thin" size:18.0f]];
}
+ (instancetype)genericRatingViewWithTitle:(NSString *)title andMessage:(NSString *)message {
return [[PCCGenericRatingView alloc] initWithTitle:title andMessage:message];
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
- (BOOL)translatesAutoresizingMaskIntoConstraints {
return NO;
}
-(void)updateConstraints {
CGRect windowRect = [UIScreen mainScreen].bounds;
NSDictionary *metrics = #{
#"height" : #(windowRect.size.height),
#"width" : #(windowRect.size.width)
};
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(self);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[self(==width)]" options:0 metrics:metrics views:viewsDictionary]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[self(==height)]" options:0 metrics:metrics views:viewsDictionary]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:35.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_titleLabel
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:30.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_messageLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:-40.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_messageLabel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_messageLabel
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_messageLabel
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:50.0f]];
[super updateConstraints];
}
#end
Parent View Controller:
#import "PCCLeaveRatingViewController.h"
#import "PCCGenericRating.h"
#import "PCCSliderRatingView.h"
#interface PCCLeaveRatingViewController ()
#end
#implementation PCCLeaveRatingViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = #[ [[PCCGenericRating alloc] initWithTitle:#"Easiness"
andMessage:#"WHAT A JOKERRRR"
andVariatons:#[ #"very easy", #"easy", #"moderate", #"hard", #"very hard"]],
];
[self initView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)initView {
[self setupConstraints];
[self addViewController];
}
- (void)setupConstraints {
CGRect windowFrame = [[UIApplication sharedApplication].delegate window].frame;
CGFloat windowWidth = windowFrame.size.width * self.dataSource.count;
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:self.containerView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:windowWidth]];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:self.containerView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:windowFrame.size.height]];
}
- (void)addViewController {
PCCGenericRating *rating = self.dataSource[0];
PCCGenericRatingView *view = [PCCGenericRatingView genericRatingViewWithTitle:rating.title andMessage:rating.message];
[view setBackgroundColor:[UIColor yellowColor]];
const CGFloat viewWidth = [UIScreen mainScreen].bounds.size.width;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(view);
NSDictionary *metrics = #{ #"viewWidth" : #(viewWidth) };
[_containerView addSubview:view];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[view(==viewWidth)]" options:0 metrics:metrics views:viewsDictionary]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[view]|" options:0 metrics:nil views:viewsDictionary]];
}
#end
print out the view property to trace whether it is pointing the view which you are referring or other [view setBackgroundColor:[UIColor yellowColor]]

Issue with autoLayout and constraints

I have an issue with iOS and AutoLayout...it is a testing application where I have replicated the problem. It consists in a simple view controller, containing a fixed UIButton; when user clicks this button, the delegate have to create a custom view, apply it positioning constraints; the view has then a method to build his content views, and the child views are placed with constraints too.
Here is the code:
//MyViewController.m
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *start_but = [[UIButton alloc] initWithFrame:CGRectMake(10, 10, 100, 40)];
[start_but setTitle:#"Draw" forState:UIControlStateNormal];
[start_but setTitleColor:[UIColor yellowColor] forState:UIControlStateNormal];
[start_but addTarget:self action:#selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:start_but];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)clickAction: (id)sender
{
localView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 400, 300)];
UIView *superview = (UIView*)localView;
superview.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:localView];
NSLayoutConstraint *constr = [NSLayoutConstraint constraintWithItem:localView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:100.0];
[self.view addConstraint:constr];
constr = [NSLayoutConstraint constraintWithItem:localView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:-50.0];
[self.view addConstraint:constr];
[localView CreateGuiInterface];
}
#end
//MyView.m
#implementation MyView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
}
return self;
}
-(void)CreateGuiInterface
{
UIView *superview = self;
self.backgroundColor = [UIColor redColor];
UIButton *but1 = [[UIButton alloc] init];
[but1 setTitle:#"Button" forState:UIControlStateNormal];
[but1 setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
but1.backgroundColor = [UIColor yellowColor];
but1.translatesAutoresizingMaskIntoConstraints = NO;
[superview addSubview:but1];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:but1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight multiplier:1.0
constant:-20.0];
[superview addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:but1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:50.0];
[superview addConstraint:constraint];
UILabel *label = [[UILabel alloc] init];
label.text = #"Label";
label.backgroundColor = [UIColor greenColor];
label.textColor = [UIColor blackColor];
label.translatesAutoresizingMaskIntoConstraints = NO;
[superview addSubview:label];
constraint = [NSLayoutConstraint constraintWithItem:label
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:but1
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:-40.0];
[superview addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:label
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:but1
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:20.0];
[superview addConstraint:constraint];
}
#end
So, the problem is that when i click iOS seems to have problem to draw the view background color. It is strange, 'cause if i don't use superview.translatesAutoresizingMaskIntoConstraints = NO and put the view in a fixed place (with initWithFrame:CGRect(100,200,300,400) for example) without using constraints, it works fine: can there be problems using constraints in child and parent views? AppleDoc said that the constraints cannot pass throughout the view barrier; so i've written my app with local-view oriented constraints....
can somebody help me?
thanks in advice
The intent with autoLayout is to never have to setFrame on any of your views. If you want to place your view programatically within your superview, you will provide constraints for that. The changes I made you will see the background red as intended. I didn't change your MyView implementation (simply added -(void)CreateGuiInterface;
within MyView.h). The changes I made to demonstrate are within your MyViewController implementation.
Try the following code in place of your viewController:
#interface MyViewController ()
#property (nonatomic, strong) MyView* localView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *start_but = [[UIButton alloc] init];
start_but.translatesAutoresizingMaskIntoConstraints = NO;
[start_but setTitle:#"Draw" forState:UIControlStateNormal];
[start_but setTitleColor:[UIColor yellowColor] forState:UIControlStateNormal];
[start_but addTarget:self action:#selector(clickAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:start_but];
// The following constraints simply place the initial start button in the top left
NSDictionary* views = NSDictionaryOfVariableBindings(start_but);
NSString* format = #"H:|-[start_but]";
NSArray* constraints = [NSLayoutConstraint constraintsWithVisualFormat:format
options:0
metrics:nil
views:views];
[self.view addConstraints:constraints];
format = #"V:|-[start_but]";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format
options:0
metrics:nil
views:views];
[self.view addConstraints:constraints];
}
- (void)clickAction: (id)sender
{
self.localView = [[MyView alloc] init];
UIView *superview = (UIView*)self.localView;
superview.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.localView];
// Here I'm placing your parent view (MyView) 50 points on right of the superview with
// a width of 400 points
NSDictionary* views = NSDictionaryOfVariableBindings(superview);
NSString* format = #"H:[superview(==400)]-(50)-|";
NSArray* constraints = [NSLayoutConstraint constraintsWithVisualFormat:format
options:0
metrics:nil
views:views];
[self.view addConstraints:constraints];
// Vertically 100 points from the top of the superview with a height of 300 points
format = #"V:|-(100)-[superview(==300)]";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format
options:0
metrics:nil
views:views];
[self.view addConstraints:constraints];
[self.localView CreateGuiInterface];
}
#end

Resources