How do you override a storyboard element/view on iOS? - 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.

Related

How do I create a SubView that pops up dynamically and expands it's superview size. (in code)

So I currently have a UIView subclass called: "InputView" with two subviews, a UILabel and a UITextField. The way i've set it up is I can change the label and text field placeholder so that this InputView can serve as a modular component for any input field in my app (email, password, etc.) All I have to do is create an empty UIView in my storyboard, set it's class as InputView, and have the rest be done programmatically.
Now, I'm running into an issue. The InputView's are plain UIView's whose constraints are set in the storyboard, but I want to add a subview (a table) that appears/disappears dynamically whenever a user is editing the UITextField. Every time the table appears, the InputView expands in height, pushes everything below it down, and then disappears very neatly afterward.
I have no idea how to do that. Here is what I've thought of doing:
Idea 1: Place the table in the view, add all necessary constraints to it and hope InputView expands by itself. This lead to the table being outside the frame of the InputView, and therefore unresponsive to user touches for some reason
Idea 2: Change the bounds of the InputView and expand it's height by the table's height. This doesn't work because the constraints are set in the storyboard and I don't have access to them programmatically
Idea 3: After almost 5 hours of research and failed attempts, go to StackOverflow and see if someone has an idea how to fix it. (Thanks!)
You can set things up so that your storyboard constraints can be changed programmatically.
1) In the storyboard, set a height constraint for inputView and set it to the value that makes sense for when the table is not visible -- probably around 30. (You have probably already done this.)
2) Then create an outlet to your code by control-dragging that height constraint to the file that corresponds to the view controller that has inputView in it. A dialog box will prompt you to give the outlet a name in your code. Call it something like inputViewHeightConstraint.
3) Then in your code, when you want to make your inputView have a bigger height, set inputViewHeightConstraint.constant to a new value. Note you need to refer to .constant in order to set the new height of the constraint.
When you first set your height constraint for inputView, be sure to make it an "equals" constraint (e.g., its height equals 30) rather than a "greater than or equals" constraint. Even though the latter would make more sense since you'll be changing it to a greater value in your code, I've found there are bugs with that approach. Making it a straight "equals" constraint works more cleanly.

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.

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.

Is it possible to get the calculated frame given an array of constraints?

I'm trying to animate a view from a thumbnail to a fullscreen view when touched. When the view is in thumbnail mode it is a subview of the UITableView.tableFooterView. When I animate to fullscreen I move the view to the controller's root view before updating the constraints. I do this because the tableview clips the subviews.
This is working perfectly, but when I try to do the reverse animation it's not so easy. I have to first move the thumbnail view back into a subview of the UITableView.tableFooterView before I can update the constraints. I then undo the constraints (basically set them to what there where originally). This works but the animation does not look right because as soon as the view is moved back into UITableView.tableFooterView it's clipped again by the UITableView and the animation is partially hidden behind all the tableview adornments!
My idea is to get the calculated frame for the constraints and perform old fashioned frame animation and then install the constraints after.
Is there a known way to ask the layout system given an array of constraints what will the frame be without actually installing those constraints?
Thanks.
I don't think there is such a way.
Note that the constraints you define (NSLayoutConstraint) are not the only constraints. Other constraints are defined by the properties of your views (e.g. intrinsic content size, hugging priorities etc.).
I guess this is something that would work better with autoresizing instead of autolayout.
Another solution would be to use a hidden placeholder view in the table footer. When you are returning the view back into the footer, you can just ask for the frame of the placeholder.

Constraints changed when modifying UIViewController.frame

I have a UIViewController which I add to another UIViewController as a child. When the child gets added, the parent set frame on my child using childVC.frame = ...., but this fubars my constraints.
I can duplicate this issue in IB easily. Create a UIViewController, change the size to be "Freeform" and ditch the status bar (just so it looks right visually). Add in a UIButton (or anything) and constraints so so they are both the same size (see image below)
Now change the size of the top level view in the view controller and watch what happens.
It changed my constraints on me. I wanted my button to just get bigger and still hold the original constraints. Something which happens automatically if you toggle between iPhone retina 3.5/4.0 in screen or change from landscape to portrait. But why not when I resize the view? This is making a royal pain to lay things out in freeform and test different sizes.
More importantly... why does the exact same thing happen when I set the frame in code when I add the child for my VC and how do I prevent it?
Frame based calculations and Auto Layout are not compatible. Or at least, they mean stormy waters. That's why you are having issues. Having switched to Auto Layout you have to accept that you no longer manipulate frame directly.
So, you cannot do childVC.frame = whatever.
Instead, you must now use Auto Layout to update the size of childVC.frame. Do that by creating - in IB - an IBOutlet that is the width constraint of childVC. Similarly, create another IBOutlet that is the childVC height constraint.
In your view controller, update the constant value to change the size.
myWidthConstraint.constant = 123.f;
The place to do this is in the controller's updateViewConstraints method, or in the case of a view, updateConstraints.
That said, it's better to set up Auto Layout so that you don't need any programmatic updates. When properly defined, it will know how to handle things with the view's bounds change. To be honest, it should only be necessary to update sizes programmatically if you want to do something more unusual.
Can I just say however that you are better

Resources