How to add custom view with autolayout programmatically - ios

My storyboard has a single view controller containing a UIScrollView. I want to add some custom view (each view will contain a image & label) to the scrollview programmatically in the viewController. I have set constraints to the UIScrollView in the storyboard but struggling to add custom views with constraints. How can i do this so that it can be appeared as same for all the devices (iPhone/iPad) ? I tried like below but it does not seems to be working.
In My ViewController.m -
#import "MainViewController.h"
#define DEFAULT_ROW_HEIGHT 50
#interface MainViewController ()
#property float topMarginFromUpperView;
#end
#implementation MainViewController
-(void) addCustomRowToView {
self.topMarginFromUpperView += DEFAULT_ROW_HEIGHT + 10.0f;
UIView *customRow = [[UIView alloc] initWithFrame:self.scrollView.bounds];
customRow.backgroundColor = [UIColor redColor];
[self.scrollView addSubview:customRow];
customRow.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeTop multiplier:1.0f constant:self.topMarginFromUpperView]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeLeading multiplier:1.0f constant:10.0f]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-10.0f]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeHeight multiplier:0.1f constant:0.0f]];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.topMarginFromUpperView = 0.0f;
for (NSInteger i =0; i< 10; i++) {
[self addCustomRowToView];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
EDITED :
Still struggling with constraint setting. App crashes due to invalid constraint. Tried as below -
-(void) setConstraints:(UIView *)customRow {
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeTop multiplier:1.0f constant:self.topMarginFromSuperView]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeLeading multiplier:1.0f constant:10.0f]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-10.0f]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:customRow attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeHeight multiplier:0.1f constant:0.0f]];
}
-(void) addCustomRowToView {
UIView *customRow = [[UIView alloc]initWithFrame:CGRectMake(DEFAULT_PADDING, self.topMarginFromSuperView, self.view.bounds.size.width - 2*DEFAULT_PADDING, DEFAULT_ROW_HEIGHT)];
customRow.backgroundColor = [UIColor redColor];
customRow.translatesAutoresizingMaskIntoConstraints = NO;
[self setConstraints:customRow];
[self.scrollView addSubview:customRow];
self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, self.topMarginFromSuperView + DEFAULT_ROW_HEIGHT);
self.topMarginFromSuperView += DEFAULT_ROW_HEIGHT + DEFAULT_PADDING;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.topMarginFromSuperView = 0.0f;
//for (NSInteger i =0; i< 10; i++) {
[self addCustomRowToView];
// }
}
#end

Here is solution for your problem:
- (void)addCustomView
{
UIView *lastView;
for (int i = 0; i<10; i++)
{
UIView *view = [[UIView alloc] init];
view.backgroundColor = i%2 ? [UIColor redColor] : [UIColor greenColor];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:view];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[view]-10-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeWidth multiplier:1 constant:-20.0]];
if (i == 0)
{
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[view(40)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(view)]];
}
else
{
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[lastView(40)]-10-[view(40)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(lastView,view)]];
}
lastView = view;
}
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[lastView(40)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(lastView)]];
}

Related

Instance variable used while 'self' is not set to the result of [(super or self) init…]

My Code :
- (instancetype)initWithSender:(UIView*)sender withSuperView:(UIView*)superView withItems:(NSMutableArray*)items
{
self = [CustomDropdownView loadInstanceFromNibWithiPad:NO];
if (self) {
self.frame = CGRectZero;
[self.layer setBorderColor:[UIColor lightGrayColor].CGColor];
[self.layer setBorderWidth:1.0];
CGRect positionInSuper = [superView convertRect:sender.frame fromView:sender.superview];
float height = superView.frame.size.height - positionInSuper.origin.y;
if (height > 200.0) {
// height = 200.0;
}
[superView addSubview:self];
self.translatesAutoresizingMaskIntoConstraints = NO;
self.constLeading = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:positionInSuper.origin.x];
self.constTop = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superView attribute:NSLayoutAttributeTop multiplier:1.0 constant:positionInSuper.origin.y + sender.frame.size.height];
self.constWidth = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:sender.frame.size.width];
self.constHeight = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0];
[superView addConstraints:#[self.constLeading, self.constTop]];
[self addConstraints:#[self.constWidth, self.constHeight]];
[self layoutIfNeeded];
self.arrItems = [[NSMutableArray alloc]initWithArray:items];
[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([DropDownTableViewCell class]) bundle:nil] forCellReuseIdentifier:NSStringFromClass([DropDownTableViewCell class])];
[self.tableView reloadData];
}
return self;
}
The first line
self = [CustomDropdownView loadInstanceFromNibWithiPad:NO];
is very problematic. You should only assign the result of another init method, usually [super init]. Change this to a class method.
By the way, that's what the error message says.

UIView breaking in iOS7 but not in iOS8

I have created a custom view to show 2 labels inside a circle. It is suppose to look like this:
but in iOS 7 it looks like this:
That happens because the constraints hat are supposed to stick the labels to the top/bottom are breaking for some reason, but only on iOS 7. I get the following error:
(
"<NSLayoutConstraint:0x7f96e500a540 H:|-(-1)-[UILabel:0x7f96e2dd84d0] (Names: '|':UIView:0x7f96e2df6350 )>" )
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7f96e500a540 H:|-(-1)-[UILabel:0x7f96e2dd84d0] (Names: '|':UIView:0x7f96e2df6350 )>
I have confirmed that those are in fact the constraints called (I only gave one error as an example)
self.dayLabelStickButtomConstraint
self.monthLabelStickTopConstraint
Here is the code for the custom view, can anyone maybe understand why is it breaking on iOS 7 but not on 8?
- (instancetype) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initiateValues];
[self buildHeirarchy];
}
return self;
}
- (instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initiateValues];
[self buildHeirarchy];
}
return self;
}
-(void)initiateValues{
self.fillColor = [UIColor blackColor];
self.month = #"jan";
self.monthFontSize = 8.0f;
self.monthTextColor = [UIColor blackColor];
self.monthLabelInsetDistance = 1.0f;
self.day = #"01";
self.dayFontSize = 12.0f;
self.dayTextColor = [UIColor blackColor];
self.dayLabelInsetDistance = 1.0f;
}
-(void)buildHeirarchy{
self.innerContainer = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 5.0, 5.0)];
[self.innerContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self.innerContainer];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.innerContainer
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.innerContainer
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.innerContainer
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.7
constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.innerContainer
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeHeight
multiplier:0.7
constant:0.0]];
self.monthLabel = [[UILabel alloc] initWithFrame:CGRectInset(self.bounds, 5.0, 5.0)];
[self.monthLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
self.monthLabel.textAlignment = NSTextAlignmentCenter;
[self.innerContainer addSubview:self.monthLabel];
[self.innerContainer addConstraint:[NSLayoutConstraint constraintWithItem:self.monthLabel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.innerContainer
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
NSLayoutConstraint *monthTopConstraint = [NSLayoutConstraint constraintWithItem:self.monthLabel
attribute:NSLayoutAttributeTopMargin
relatedBy:NSLayoutRelationEqual
toItem:self.innerContainer
attribute:NSLayoutAttributeTopMargin
multiplier:1.0
constant:self.monthLabelInsetDistance];
self.monthLabelStickTopConstraint = monthTopConstraint;
[self.innerContainer addConstraint:self.monthLabelStickTopConstraint];
self.dayLabel = [[UILabel alloc] initWithFrame:CGRectInset(self.bounds, 5.0, 5.0)];
[self.dayLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.innerContainer addSubview:self.dayLabel];
self.dayLabel.textAlignment = NSTextAlignmentCenter;
[self.innerContainer addConstraint:[NSLayoutConstraint constraintWithItem:self.dayLabel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.innerContainer
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
NSLayoutConstraint *dayButtomConstraint = [NSLayoutConstraint constraintWithItem:self.dayLabel
attribute:NSLayoutAttributeBottomMargin
relatedBy:NSLayoutRelationEqual
toItem:self.innerContainer
attribute:NSLayoutAttributeBottomMargin
multiplier:1.0
constant:self.dayLabelInsetDistance];
self.dayLabelStickButtomConstraint = dayButtomConstraint;
[self.innerContainer addConstraint:self.dayLabelStickButtomConstraint];
}
-(void)layoutSubviews{
// get the graphics context
if (!self.backgroundColorRoundView) {
self.backgroundColorRoundView = [[UIView alloc]initWithFrame:self.bounds];
[self addSubview:self.backgroundColorRoundView];
[self sendSubviewToBack:self.backgroundColorRoundView];
self.backgroundColorRoundLayer = [CAShapeLayer new];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:self.backgroundColorRoundView.bounds];
self.backgroundColorRoundLayer.path = path.CGPath;
//imageMaskLayer.fillColor = self.fillColor.CGColor;
[self.backgroundColorRoundView.layer addSublayer:self.backgroundColorRoundLayer];
}
self.backgroundColorRoundView.frame = self.bounds;
self.backgroundColorRoundLayer.frame = self.bounds;
[self updateViews];
[super layoutSubviews];
}
-(void)updateViews{
self.monthLabel.textColor = self.monthTextColor;
self.dayLabel.textColor = self.dayTextColor;
self.backgroundColorRoundLayer.fillColor = self.fillColor.CGColor;
}
-(void)updateConstraints{
self.dayLabelStickButtomConstraint.constant = self.dayLabelInsetDistance;
self.monthLabelStickTopConstraint.constant = self.monthLabelInsetDistance;
[super updateConstraints];
}
After a lot of messing around the answer lied in the restraints themselves. Once i switched from topMargin,bottomMargin to just top and bottom it worked

iOS Auto layout Rows

I'm trying to understand auto layout better and I created some static methods that can help me out. Now I'm trying to setup an array of views in like a tableview structure (in rows). This works kinda.
See here my problem:
To achieve this I use this code:
//In the update constraints method
UIView *view1 = [self createView];
view1.backgroundColor = [UIColor redColor];
UIView *view2 = [self createView];
view2.backgroundColor = [UIColor yellowColor];
[self addSubview:view1];
[self addSubview:view2];
[self addConstraints:[DXVFL row:#[view1, view2] inView:self]];
//The row method
+ (NSArray *) row:(NSArray const *)views inView:(UIView const *)superview {
NSMutableArray *constraints = [NSMutableArray new];
CGFloat multiplier = 1.f / [views count];
UIView *prev = nil;
for (UIView *view in views) {
if (!view.hidden) {
[constraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeWidth multiplier:1.f constant:0.f]];
[constraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeHeight multiplier:multiplier constant:0.f]];
if (prev) {
[constraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:prev attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]];
}
prev = view;
}
}
return [constraints copy];
}
So this part works (I guess). But when I want to split the yellow view again in rows. The views are always added into the top region?
In the update constraints method I do this:
UIView *view1 = [self createView];
view1.backgroundColor = [UIColor redColor];
UIView *view2 = [self createView];
view2.backgroundColor = [UIColor yellowColor];
[self addSubview:view1];
[self addSubview:view2];
UIView *view3 = [self createView];
UIView *view4 = [self createView];
[view2 addSubview:view3];
[view2 addSubview:view4];
[self addConstraints:[DXVFL row:#[view1, view2] inView:self]];
[view2 addConstraints:[DXVFL row:#[view3, view4] inView:view2]];
But I want those two rows in the bottom? What am I doing wrong?
Off the top of my head:
You need to add a constraint to tell it to align the view to the top of the superview. At the moment, you just set the height and width but you don't tell it where to put its top edge.
Try changing your code to this:
if (!prev) {
[constraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]];
} else {
[constraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:prev attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]];
}

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

ios AutoLayout issue for flexible width?

I think this is an issue of autolayout.
I create a view, and create some subviews in it.
The content view contains the subviews, and the subviews lays all in one line.
I think I didn't do something wrong, but the content view 's frame always incorrect.
like this:
2013-08-29 14:13:34.688 AutoLayoutBugTest[4658:c07] pV_content.frame {{0, 208}, {319, 44}}
The content view's width is 319 but not 320, change the 'cnt' param in viewDidLoad function, the frame may worse.
code like this:
#interface ViewController ()
#property (nonatomic, strong) UIView *pV_content;
#property (nonatomic, strong) NSMutableArray *Arr_views;
#end
#implementation ViewController
- (void)removeConstraintForCustomView:(UIView *)customView
fromView:(UIView *)superView
{
NSMutableArray *pArr_constraint = [NSMutableArray array];
for (NSLayoutConstraint *constraint in superView.constraints) {
if ([constraint.firstItem isEqual:customView]) {
[pArr_constraint addObject:constraint];
}
}
[superView removeConstraints:pArr_constraint];
pArr_constraint = nil;
}
- (void)constraintCustomView:(UIView *)customView
matchWidthOfView:(UIView *)superView
withHeight:(CGFloat)height
{
[self removeConstraintForCustomView:customView fromView:superView];
customView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint_y = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeCenterY
multiplier:1.f
constant:0.f];
[superView addConstraint:constraint_y];
NSLayoutConstraint *constraint_height = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeHeight
multiplier:0.f
constant:height];
[superView addConstraint:constraint_height];
NSLayoutConstraint *constraint_left = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeLeft
multiplier:1.f
constant:0.f];
[superView addConstraint:constraint_left];
NSLayoutConstraint *constraint_width = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:1.f
constant:0.f];
[superView addConstraint:constraint_width];
}
- (void)constraintCustomView:(UIView *)customView
matchWidthPercentOfView:(UIView *)superView
boxCount:(int)boxCount
boxIndex:(int)boxIndex
{
[self removeConstraintForCustomView:customView fromView:superView];
customView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint_top = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeTop
multiplier:1.f
constant:customView.frame.origin.y];
[superView addConstraint:constraint_top];
constraint_top.priority = UILayoutPriorityFittingSizeLevel;
NSLayoutConstraint *constraint_height = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeHeight
multiplier:0.f
constant:customView.frame.size.height];
[superView addConstraint:constraint_height];
constraint_height.priority = UILayoutPriorityFittingSizeLevel;
if (boxCount == 1) {
NSLayoutConstraint *constraint_percent_x = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeCenterX
multiplier:1.f
constant:0.f];
[superView addConstraint:constraint_percent_x];
constraint_percent_x.priority = UILayoutPriorityFittingSizeLevel;
NSLayoutConstraint *constraint_width = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:1.f
constant:-4.f];
[superView addConstraint:constraint_width];
constraint_width.priority = UILayoutPriorityFittingSizeLevel;
}
else {
if (boxIndex == 0) {
NSLayoutConstraint *constraint_left = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeLeft
multiplier:1.f
constant:0.f];
[superView addConstraint:constraint_left];
constraint_left.priority = UILayoutPriorityFittingSizeLevel;
NSLayoutConstraint *constraint_width = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:(2.f / (CGFloat)(2 * boxCount + 1))
constant:-2.f];
[superView addConstraint:constraint_width];
constraint_width.priority = UILayoutPriorityFittingSizeLevel;
}
else if (boxIndex == boxCount - 1) {
NSLayoutConstraint *constraint_right = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeRight
multiplier:1.f
constant:0.f];
[superView addConstraint:constraint_right];
constraint_right.priority = UILayoutPriorityFittingSizeLevel;
NSLayoutConstraint *constraint_width = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:(2.f / (CGFloat)(2 * boxCount + 1))
constant:-2.f];
[superView addConstraint:constraint_width];
constraint_width.priority = UILayoutPriorityFittingSizeLevel;
}
else {
NSLayoutConstraint *constraint_percent_x = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeCenterX
multiplier:(((CGFloat)(2 * boxIndex + 1)) / (CGFloat)boxCount)
constant:0.f];
[superView addConstraint:constraint_percent_x];
constraint_percent_x.priority = UILayoutPriorityFittingSizeLevel;
NSLayoutConstraint *constraint_width = [NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:(2.f / (CGFloat)(2 * boxCount + 1))
constant:-2.f];
[superView addConstraint:constraint_width];
constraint_width.priority = UILayoutPriorityFittingSizeLevel;
}
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor whiteColor];
self.pV_content = [[UIView alloc] initWithFrame:CGRectMake(0.f,
0.f,
self.view.frame.size.width,
44.f)];
self.pV_content.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:self.pV_content];
[self constraintCustomView:self.pV_content
matchWidthOfView:self.view
withHeight:44.f];
int cnt = 5;
for (int i = 0; i < cnt; i++) {
UIView *pV_sub = [[UIView alloc] initWithFrame:CGRectMake(0.f,
5.f,
30.f,
35.f)];
pV_sub.backgroundColor = [UIColor redColor];
[self.pV_content addSubview:pV_sub];
[self constraintCustomView:pV_sub
matchWidthPercentOfView:self.pV_content
boxCount:cnt
boxIndex:i];
}
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
NSLog(#"pV_content.frame %#", NSStringFromCGRect(self.pV_content.frame));
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
#end
From guide:
Constraints are of the form "view1.attr1 view2.attr2 * multiplier + constant". If the constraint you wish to express does not have a second view and attribute, use nil and NSLayoutAttributeNotAnAttribute.
And secondary: Try use
constraintsWithVisualFormat:options:metrics:views:
For example if you have a view testView:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[testView]|" options:0 metrics:0 views:#{#"testView":self.testView}]];
in constraintsWithVisualFormat method you can set priority and variants like:#"H:|-(0#100)-[testView(100)]-(0#100)-|". This testView have hight priority, because priority parameter not set, but 2 constraints from left and right size to superview have priority 100, and your view testView not change self frame.
You can set format like this: [testView(50,100)] in this your code means: "testView widht can be 50 points or 100.
Read this guide

Resources