Xcode Custom view constraints not updating when updating size programmatically - ios

I have a custom UIView that has 2 UILabel's which I have designed in the XIB. Have set the correct constraints to the sub items making them right aligned etc. In the XIB, my desgin was based on the assumption that screen width is 200 and constraints of the 2 labels are set to their superview accordingly.
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil];
[self addSubview:self.view];
//_intrinsicContentSize = self.bounds.size; // tried to return this as well
}
return self;
}
Now I programmatically alloc this view and and add it as subview to my view controller. If I change the frame of the view to say 300, the sub view frame gets changed to 300 but the 2 labels which have constraints follow a width of 200 only.
vewOneTouch = [[OneTouchView alloc] initWithFrame:CGRectMake(0, CGRectGetMinY(vewBottom.frame)-50, CGRectGetWidth(scwBaseView.frame), 50)];
[scwBaseView addSubview:vewOneTouch];
When the custom view frame is changed, why are the constraints not kicking in and changing the layout of all the items in the subview?
The white area is the yellow view in the Xib, have changed the color programmatically.

Related

Making a UIView keep its size when used as a table header view

If I use the following code to add a tableHeaderView to a table...
UILabel *label = [UILabel new];
label.translatesAutoresizingMaskIntoConstraints = NO;
label.text = #"Hello, world!";
self.tableView.tableHeaderView = label;
The result is that the content of the table is pushed down by the height of the label.
This is fine and is what I want.
However, when I use my own custom view (set up in code) which has its own subviews etc... then the content of the table is not pushed down and the header view overlaps onto the content.
I'm not using AutoLayout to place the subviews of my custom view because of the nature of the view it doesn't really work as an AutoLayout view.
The frame of my custom view is (0, 0, 0, 0) with the content of the header view being drawn outside its bounds.
I have implemented the - (CGSize)sizeThatFitsSize:(CGSize)size method of my custom view but that hasn't changed anything.
Is there something else I need to implement also?
Example
If I use the following code to put a label inside my custom view...
- (instancetype)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
UILabel *label = [UILabel new];
label.text = #"Hello, world!";
[label sizeToFit];
[self addSubview:label];
label.frame = CGRectMake(0, 0, CGRectGetWidth(label.bounds), CGRectGetHeight(label.bounds));
}
return self;
}
Then the label appears in the same place as before but the content is not pushed down.
The frame of the view when you set it as the table header view is the size that the table view allocates and maintains for the view.
So, you need to determine the required size for your header view and change it's frame before setting it.
The table view will not make and queries or size changes for you. Autoresizing rules do apply so if the table view frame changes the header view will be resized according to the rules you specify. This may lead to different issues.

Not able to increase height of uiview using it's subview height?

I am using autolayout for set height of uiview depends on it's subview.
I am using Reusable view.
I have added that view in my viewcontroller is here is code
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray *arrNib = [[NSBundle mainBundle] loadNibNamed:#"ReUsableView" owner:self options:nil];
UIView *subView = [arrNib objectAtIndex:0];
[self.view addSubview:subView];
lbl1 = [subView viewWithTag:1];
lbl1.text = #"fjadsk jkdjf kasdjf kasjdfkas djfkjads kfjaksd fjaskdjf kdsajf ksdjf ksdaj fksadjfkadsjf ksadjfksajd fkdsjfkajf kcnvmjkdalsjfk vaksdjfkj kfdvnkdsjfkajfkda djfkajsdfksja dfksjdafkjas;fjsk";
}
I got output like this
When I will remove bottom constaint of lbl first then I got following output.
How can I increase the height of uiview depends on lbl first height?
Appreciate for help!
Considering that you want to increase the size of your blue view with lbl, You have to do following things
Give the fixed height constraints to the lbl1.
Remove height constraint to container view.
Add equal height constraints to container view & lbl1
Change the multiplier property of equal widths constraints by
calculating containerView height / lbl1 height ("/" means divide by)
Take outlet of height constraints of lbl1.
Calculate the changed height of lbl1.Assign this to height
constraints of lbl by setting constraints.constant property.
to make this happen, required constraints for view is;
top=view.top
leading = view.leading
trailing = view.trailing
bottom >= view.bottom
lhs of equations belongs to superview of your view.
for label inside;
lbl.top = view.top
lbl.leading = view.leading
lbl.trailing = view.trailing
lbl.bottom = view.bottom.
i built it in storyboard; here is the screenshot. view size is getting bigger when the text inside the label is getting longer.

Resize imageview in its frame

I am trying to create a view that has 4 children
1 imageview
3 labels
The ImageView is supposed to take up the entire frame, and it doesnt
I've added layout constraints to it so that it would adjust to the sides.
However, i am initiating the view with different frames, and the image view size doesnt change
How do i make the image take up the entire purple area, of which it is a subview?
This is my xib file:
and this is how it looks on the device screen
the purple view is the frame i allocated for the given view:
and the green view is the entire screen
my init code:
NSArray *topViews = [[NSBundle mainBundle]loadNibNamed:#"BagView" owner:self options:nil];
[self addSubview:topViews[0]];
[self.shoppingBag setFrame:self.bounds];
NSArray *topViews = [[NSBundle mainBundle]loadNibNamed:#"BagView" owner:self options:nil];
UIView *v = topViews[0];
v.frame = self.bounds;
[self addSubview:v];
fixed the problem
Set the contentMode property of UIIMageView to UIViewContentModeScaleToFill
[your_imageview setContentMode: UIViewContentModeScaleToFill];

Programatically creating a UIView with multiple Labels

Xcode Interface Builder issue
Personally I do not like the way that interface builder works in Xcode. In this example I am trying to create a fairly complex view controller. On the viewDidLoad of the view controller I show a custom alert view (as such). It is not actually an alert view but more of a view that shows the user some information. I have a dimmed background view and a view on top of this. If I try to create this in interface builder it gets overly complicated as you cannot select the views in the background and move them etc without dropping subviews into the wrong views and so on...
Scenario
What I am trying to do is create a View which holds some labels and a button. The view controller has a difficulty property based on this it will have different text in the labels/amount of labels.
I.e. Easy -- 3 labels
Hard -- 4 labels
I create the dimmedView and alert(styled)View like this:
// Setup the dimmedView
UIView *dimmedView = [[UIView alloc] initWithFrame:self.view.frame];
dimmedView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.6];
// Setup the startingAlertView
UIView *startingAlertView = [[UIView alloc] init];
startingAlertView.backgroundColor = [UIColor whiteColor];
I then create the three/four labels based on some logic and add the necassary labels to the startingAlertView based on logic also.
The issue that is obvious is that at no point a frame for the view is set. This means that it is returning 0,0,0,0. What I would like to happen is the view to take the required height based on the labels added.
I am building for IOS7 and using Auto Layout. Should I be setting up constraints which would then adjust the relevant heights and locations in the view possibly?
I am building for IOS7 and using Auto Layout. Should I be setting up constraints which would then adjust the relevant heights and locations in the view possibly?
Yes. you don't use initWithFrame: under auto layout, or rather, you can, but the frame is ignored. Create your dimming view with a frame of CGRectZero, setting translatesAutoresizingMasksToConstraints to NO, add it to your main view and create constraints pinning it to all edges of the superview.
Then, add your alert view, again with a frame of zero and the translates... property set to NO. Create constraints to centre this view in your dimming view. This view will get its size from its subviews, since labels have an intrinsic size.
Add your labels as subviews of this view, with frame of zero and translates... set to NO. Depending on their content you may wish to set preferred max layout width or a width constraint.
Create constraints pinning your labels to the left and right edges of the superview, and lining your labels up in a vertical 'stack'. In each case you could add padding to give your alert a bit of a border.
This can look like a large amount of code, so you may want to read the articles I've written on visual format for auto layout and creating constraints in code, with the associated autolayout convenience category to make your life easier.
If you're going to the auto layout route, then you can add constraints that will keep the proper space between each label, and the proper space between the top and bottom of the view with the first and last labels. However, if you're not doing this in Interface Builder, you might as well skip using auto layout also, because it's fairly simple to just adjust the height of the view as you add labels.
You would start by setting the height of the view to the size of the top and bottom spaces that you want to have around the labels. Then each time you add a label, add to it the height of the label plus the height of the space you're putting between labels.
You could also wait until you've added all of the labels that you want, then set the height to the bottom label's y position plus its height plus the bottom space you want to have around the labels.
Yes, using autolayout you can get the bounds from the parent view.
Here is a quick example, notice that we are not using frame, and using CGRectZero for our UILabels, the positioning comes from updateConstraints instead. I am using Visual Format Language to layout the labels which I recommend if you are doing it programatically.
Here we are making the labels the width of the parent view and then just stacked on top of each other.
#import "View.h"
#implementation View{
UILabel *_label1;
UILabel *_label2;
UILabel *_label3;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_label1 = [[UILabel alloc] initWithFrame:CGRectZero];
_label1.translatesAutoresizingMaskIntoConstraints = NO;
_label1.text = #"LABEL 1";
_label2 = [[UILabel alloc] initWithFrame:CGRectZero];
_label2.translatesAutoresizingMaskIntoConstraints = NO;
_label2.text = #"LABEL 2";
_label3 = [[UILabel alloc] initWithFrame:CGRectZero];
_label3.translatesAutoresizingMaskIntoConstraints = NO;
_label3.text = #"LABEL 3";
[self addSubview:_label1];
[self addSubview:_label2];
[self addSubview:_label3];
}
[self updateConstraintsIfNeeded];
return self;
}
-(void)updateConstraints
{
[super updateConstraints];
NSDictionary *_viewsDictionary = NSDictionaryOfVariableBindings(_label1,_label2,_label3);
// Set the contraintsto span the entire width of the super view
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label1]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label2]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label3]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
// Last setup the vertical contraints other wise they will end up in a random place
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[_label1]-[_label2]-[_label3]"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end

Loaded UIView from NIB - Wrong Frame Size

Ok, so I've created a UIView in interface builder. I'm using AutoLayout and I've got one subview of this view pinned to all four sides.
Here's what I don't understand. When I load this NIB file using loadNibNamed. I then get a reference to the view. I set the frame for this view. And yet, when I access the subview (using [containerView viewWithTag:1]) it's frame hasn't been automatically resized. What gives? If you change the frame for a parent view, why wouldn't the subview frame change as well?
It doesn't make any sense.
Why can't you just load a UIView, set it's frame and have all the subviews adjust as appropriate (ESPECIALLY since I'm using AutoLayout!)?
EDIT: To be clear, all I want to do is be able to define a UIView hierarchy in IB with appropriate AutoLayout constraints and then be able to load and display that view on the screen sometimes at different sizes? Why is this so hard?
UIKit doesn't update subview geometry immediately when you change a view's geometry. It batches up the updates for efficiency.
After running your event handler, UIKit checks whether any views in the on-screen window hierarchy need to be laid out. If it finds any, it lays them out by solving your layout constraints (if you have any) and then sending layoutSubviews.
If you want to solve the constraints and lay out a view's subviews immediately, simply send layoutIfNeeded to the view:
someView.frame = CGRectMake(0, 0, 200, 300);
[someView layoutIfNeeded];
// The frames of someView.subviews are now up-to-date.
I too had the same problem, I was creating a tutorial view where in which I wanted to add multiple UIViews to a scrollview. While I was trying to get the frame from xib, it gave always 320 and because of that the offset for the pages were wrong and my views looked crappy in iPhone6 and 6plus.
I then used pure autolayout approach, ie instead of using the frame, I added constraints through VFL so that subviews fit exactly to the superview. Below is the snapshot of code where I create around 20 UIViews from Xib and add properly to scrollview
Full code here ScrollViewAutolayout
Method to layout the childviews in the scrollview.
#param nil
#result layout the child views
*/
-(void)layoutViews
{
NSMutableString *horizontalString = [NSMutableString string];
// Keep the start of the horizontal constraint
[horizontalString appendString:#"H:|"];
for (int i=0; i<viewsArray.count; i++) {
// Here I am providing the index of the array as the view name key in the dictionary
[viewsDict setObject:viewsArray[i] forKey:[NSString stringWithFormat:#"v%d",i]];
// Since we are having only one view vertically, then we need to add the constraint now itself. Since we need to have fullscreen, we are giving height equal to the superview.
NSString *verticalString = [NSString stringWithFormat:#"V:|[%#(==parent)]|", [NSString stringWithFormat:#"v%d",i]];
// add the constraint
[contentScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:verticalString options:0 metrics:nil views:viewsDict]];
// Since we need to horizontally arrange, we construct a string, with all the views in array looped and here also we have fullwidth of superview.
[horizontalString appendString:[NSString stringWithFormat:#"[%#(==parent)]", [NSString stringWithFormat:#"v%d",i]]];
}
// Close the string with the parent
[horizontalString appendString:#"|"];
// apply the constraint
[contentScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:horizontalString options:0 metrics:nil views:viewsDict]];
}
Unfortunately the accepted answer by Rob didn't work for me. This is what worked:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"myXib" owner:self options:nil];
[self addSubview:views[0]];
self.subviews[0].frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); //ADDED THIS FOR PROPER SIZE
}
return self;
}

Resources