When is the frame size of a UIButton finally set - ios

I made a custom subclass of UIButton so I can add an extra label. When this button is in an UIStackView, the extra label is not being sized properly. I tried programmatically adding constraints to parent button. This worked but I had pages of broken constraint warnings (slight exaggeration). If I knew when the button has been assigned a final size, I can set the added labels frame.
When is the size of a UIButton known after all the auto layout stuff is done?

As per my comments above, the frame of a button should be known and can be used when layoutSubviews() is called, this can be overridden in your custom class

viewDidLayoutSubViews is the method which finish last. While assigning auto layout constraints it gives you proper Rect information. You can use this to set constraints.

Try this: put "print("frame is: ", button.frame," in method name")" in the following methods.
viewDidLoad
viewDidLayoutSubviews
viewWillLayoutSubviews
viewWillAppear
viewDidAppear
You'll notice that the actual final frame of the button doesn't happen until the final call of viewDidLayoutSubviews and then viewDidAppear, and that viewDidLayoutSubviews is called a number of times before that, which is especially annoying when you're trying to resize the font on this label according to the size of the button's title label.
If the stack view is messing with your sizing, drag a UIView into the stack view and put the button in it, then you can constrain the button to that view.

Related

Adjusting Subclassed UITableViewCell UILabel Frame

I have a subclass UITableViewCell. In storyboard I added some labels and buttons and created respective outlets, and I set and tweaked their respective frames using the size inspector. The layout looks good for a iPhone 6. On an iPhone 5 things are off screen on the right. In the layoutSubviews method of the UITableView subclass I attempt to adjust the frame of my labels and buttons. When the tableview first appears the buttons and labels are in the positions specified for their respective frames in Storyboard and not in the frame/position I set in layoutSubviews. A moment later they appear in the positions set in layoutSubviews. Sometimes however, I see it revert to the old storyboard set position.
Any ideas what might be happening? I want to set the labels and button frames explicitly and have them stay where I put them. I don't want to use constraints as they are really painful to use in Storyboard, and frequently don't behave well or with good granularity.
If you do not use constraints, please remove use auto lay out and use size class.

TableView and autolayout, wrong frame for headers

I'm adding a view controller with a table view as a subview of a main view controller. I'm setting the constraints for position and size, i'm not givin a specific frame for the view, but i noticed that the header's labels are in a wrong frame, they have a x position of -30, instead of zero.
This happens only if i use constraints, if i use normal setframe method this problem doesn't happen.
I've tried with layoutIfneeded, or layoutSubviews but nothing seems to work

iOS AutoLayout - when does it Run?

I have a XIB file where I specify constraints at design time.
In addition to controls with constraints, I also have controls that have no constraints in the XIB that I want to position programmatically at run time.
I do this by repositioning views manually in didRotateFromInterfaceOrientation.
When I rotate the device, my code places the controls that are manually controlled but then later it appears that auto layout messes up the placement of manual controls that have no constraints on them in the Interface builder.
Question - When exactly does Auto layout run if I rotate the device.
Auto Layout runs at the end of the run loop after setNeedsLayout is called on a view, or immediately when setNeedsLayout is followed by layoutIfNeeded. Note that there are many methods that may call setNeedsLayout in their implementation, such as addSubview:, removeFromSuperview, etc.
A couple suggestions:
Instead of updating your constraints in didRotateFromInterfaceOrientation:, try doing so in updateViewConstraints.
You may also want to add placeholder constraints in Interface Builder to the views you will position programmatically. I think IB will automatically constrain views to their x/y position if a placeholder constraint is not set, but I'm not 100% sure on that.
The important thing to know is that the results of constraint calculations are applied in layoutSubviews. So if you want to set frames, you do so after calling super in layoutSubviews or in your view controller's viewDidLayoutSubviews.
Having said that, I've only experimented with this. I've not done it in production code and can't say that you won't run into issues.
I'm actually not clear on whether it's OK to set frames manually like this. The only relevant bit of information I've come across is the following from Apple's Auto Layout documentation:
You cannot set a constraint to cross the view hierarchy if the
hierarchy includes a view that sets the frames of subviews manually in
a custom implementation for the layoutSubviews method on UIView (or
the layout method on NSView).
Here, the phrase "view that sets the frames of subviews manually" implies to me that manually setting frames is OK. I'd be interested to know if anyone has a see a more explicit discussion on this.
you must not change the frame you must connect the constraint with iboulet and in the didRotateFromInterfaceOrientation you can change the constraint like this:
(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
if (UIInterfaceOrientationIsLandscape(fromInterfaceOrientation)) {
self.constraintTop.constant = 50;
}else{
self.constraintTop.constant = 100;
}
}

Why does UIButton resize when I access titleLabel property?

I'm trying to adjust the size of a button to (it's intrinsic size + a little bit more) in order to draw a custom background. However, every time I access self.titleLabel within the button, the size and position resnaps to that of the storyboard. I don't have to do anything with the label to reproduce this, just retrieve it from the button.
I've put logging code all over my button and view controller in order to find where this is happening. It's not coming from a relaying-out of subviews or any other notification I see to get within the view controller. The line before accessing titleLabel, the position and size are correct. The line after, it has snapped back to the storyboard position. Commenting out the access prevents the size/position snapping. Can someone tell me where or why this is happening?
I have no constraints set (that I can tell), but am I fighting against auto-layout here? Should I be doing this a different way like composing controls or something?
Something similar (or the same?) has been asked before at UIButton modifying titlelabel seems to change its frame and UIButton titleLabel resizes on press?, but both were left unanswered or explained away with just "maybe a bug."
If the project has auto-layout enabled, then YES, you're fighting auto-layout. You have two choices, either subclass UIButton so that you can override the intrinsic size calculation, or modify the constraints so that the intrinsic size is not used in any constraint. If you do the latter, then you probably want to create an IBOutlet to the constraint for the width, so that you can adjust the constant property as needed.
This isn't a bug, it's a consequence of auto layout. When using auto layout, you shouldn't set any frames. Instead, you should change the size or position by modifying the constraints. What's happening, is that whenever the view needs to be redrawn, the frame reverts to the frame that's defined by the constraints.

How do you override a storyboard element/view on iOS?

I have a UITextView set in my storyboard at a certain position. When the app first loads up, I would like the textView to be at a different position then where it is in the storyboard. I do this programmatically by setting its frame to a different frame with a different origin (later the textView will be animated back to the original position). But no matter how I do it, the app always starts up with the textView in its storyboard assigned position. I can't seem to do this anywhere... viewDidLoad:, viewWillAppear:, nothing works. How can I override it?
One other thing... lets say the textView has a different origin then it does in its storyboard (the frame was reset at some point in the app). If I add a subview to the textview using addSubView:, the textView resets to its original position in the storyboard. Why?! Any way to stop this?
If you want to change the position of the textView when it starts up, I'd suggest that you do not have it in the storyboard and instead add it to the subview with its correct frame.
Especially with Autolayout in effect, having it there already means you would have to modify its constraints and if you're modifying its position programmatically already anyway, you might as well add it to the view programmatically too where you have more control over its constraints programmatically as well.
Under auto layout you can't set frames - well, you can, but as soon as another layout pass takes place, the frame will be reset to that defined by the constraints.
The simplest way to change the size or position of views you've laid out in interface builder using auto layout is to create outlets to the various constraints, and then edit their constant properties. If that doesn't give you enough flexibility, you still need outlets to the constraints, but when you want to move things you'll have to delete those constraints and add new ones.

Resources