One feature of my app is something that does automatic cropping of an image.
The basic idea is that someone would take a picture of a piece of paper (think: receipt), and then the image could get cropped automatically, after the borders of the paper are determined.
I'm able to determine the paper's border by using OpenCV. So, the next thing I do is to change the "center" property of each of my guides (just 2 horizontal and 2 vertical "lines" that can get dragged around manually).
Then, sometime shortly after I make all my calls to change each of the 4 guides, something else comes along and sets the "center" again. (I've overridden "setCenter" to prove this). The center seems to be reset by this: [UIView(Geometry) _applyISEngineLayoutValues].
I can't figure out why this is happening, or how to stop it, but it probably has to do with constraints. My view is a simple UIButton. When the user taps & drags on it with their finger, an action routine gets called that just changes the center. This works.
But in another case, I'm bringing up a UIImagePickerController. After they choose the picture, I determine the paper-bounds, change the "guides" centers, and then later on "_applyISEngineLayoutValues" sets them all back.
Any idea what's going on in this case? Or how I can set the center of a view, and have it actually stay?
The first rule of AutoLayout is that you can't update the frame, bounds or center of a view directly.
You must update the constraints related to the view so that the constraints update the view.
For instance, you first vertical line will have horizontal constraints something like...
1. Leading edge to superview = some value.
2. Width = some value.
This is enough (horizontally) to place this line on the screen.
Now, if you want to move this line to the right you can't just change the center you must do this...
1. Create a property in you view controller like this...
#property (nonatomic, weak) IBOutlet NSLayoutConstraint *verticalLine1LeadingConstraint;
// or if you're coding the constraint...
#property (nonatomic, strong) NSLayoutConstraint *verticalLine1LeadingConstraint;
2. Save the constraint in to that property...
// either use IB to CTRL drag the constraint to the property like any other outlet.
// or something like...
self.verticalLine1LeadingConstraint = [NSLayotuConstraint ... // this is the code adding the constraint...
[self.view addConstraint:self.verticalLine1LeadingConstraint];
Now you have a property pointing to this constraint.
Now, when you need to "update the center" of the vertical line 1...
// Calculate the distance you want the line to be from the edge of the superview and set it on to the constraint...
float distanceFromEdgeOfSuperview = // some calculated value...
self.verticalLine1LeadingConstraint.constant = distanceFromEdgeOfSuperview;
[self.view layoutIfNeeded];
This will update the position of the view and you won't get any errors.
You're using auto layout, so Fogmeister's answer is the right one, but not everyone can use auto layout - e.g. people who have to support the iPad 1 - so I'll leave this answer here.
If you need to use a view's frame but the system is adding constraints, then there is a workaround; but it's not pretty.
_applyISEngineLayoutValues sets your view's center and bounds, but doesn't touch frame. If you override setCenter: and setBounds: to do nothing, and then always use setFrame: in your own code, then _applyISEngineLayoutValues will leave you alone.
I'm not happy with this approach, but it's the only way I've found so far to stop _applyISEngineLayoutValues from pooing all over my layout logic.
Related
I am making a score marker for a game. As the score grows I want the marker to grow so it can accommodate the extra digits that are being added. I want the resizing to be smooth and also animate the new score that is displayed. At first I was using a UILabel but later found out I can't animate the background and the text independently (because they are in the same layer).
I decided to subclass UIView and add a CATextLayer as a sub layer so I can now animate the background (the UIView layer) and the text (the CATextLayer sub layer) independently.
It works great except for one thing, I placed the marker at the center bottom of of the screen like this:
the problem is that I get an error saying I should add constraints for x and y position or width and height. Since I want the view to be able to grow I can't add size constraints, and since it's is relative I can't add x or y constraints. Using an inequality constraint is useless as the error still occurs.
I know this isn't a problem using ILabel, UIButton or some other subclass of UIView. I guess I could subclass UILabel instead and just leave the text empty to achieve the same effect plus I won't have to worry about those constraints, but that feels somewhat wrong. Besides UILabel already is a subclass of UIView so I guess there probably is an option to avoid having to set size constraints.
Is there something I can do to still subclass UIView or should I just subclass UILabel or something like that and get it for free.
Try CONTENT HUGGING PRIORITY. or CONTENT COMPRESSION RESISTANCE PRIORITY. Here is a tutorial to understand about them clearly, if you are not familiar with them.
Hugging priority Vs Compression resistence
There is another option to try. If getting auto layout errors is the only problem you are facing, except everything is fine, you can use Place holder constraints.
I have in my app one UITextField on the left and one UIButton on the right. The textfield is anchored on the left at the superview (a container view) and in the right to the button.
So in the left of textfield there is a
leading space = 0 in relation of container
and on the right a
trailing space = 0 in relation of button
but if I move the button on the right way, changing the x origin value, why the textfield don't enlarge its width?
(obviously the button has its constraints about width and height and for position, but not that lock the textfield)
so if I do this
self.mybutton.frame = CGRectMake(self.mybutton.frame.origin.x+100, self.mybutton.frame.origin.y, self.mybutton.frame.size.width, self.mybutton.frame.size.height);
the button moved in the right direction but the textfield seems to doesn't enlarge its width,.
Do you know why?
Working with both Auto Layout and programmatic positioning/sizing can create a lot of headaches. Part of this is because you have created constraints in Auto Layout, which are basically "rules" that your app must follow when laying out all of it's views, and when you change the frame, bounds, or center properties you may be invalidating those rules. But since Auto Layout is not constantly recalculating the layout of your views, problems may go unnoticed until a layout recalculation is triggered.
So to answer your question, changing the frame of the button does not change the text because Auto Layout has no idea that anything has changed. Plus you haven't changed the constraints on the button so if you did call - (void) setNeedsUpdateConstraints on your text field and button, the change you are looking for won't happen. The button will move back to it's initial position, the one you set with constraints.
What you may want to do is create an IBOutlet on whatever is controlling how the button gets positioned on the x-axis (i.e. its trailing space...if that is what you are using). Then instead of doing:
self.mybutton.frame = CGRectMake(self.mybutton.frame.origin.x+100, self.mybutton.frame.origin.y, self.mybutton.frame.size.width, self.mybutton.frame.size.height);
You could do something like:
self.mybuttonXconstraint.constant = self.mybuttonXconstraint.constant + 100
[self.parentView setNeedsUpdateConstraints]
The second line is to ensure that Auto Layout knows a constraint has been changed and that it should recalculate the layout for any views involved with the parent's constraints.
This may be of interest to you as well - iOS Developer Library - Auto Layout Guide
Could you try animating the button's trailing constraint?
Like so (I changed the constraint inside an animation block for illustration purposes):
UIView.animateWithDuration(
5.0,
animations: {
self.buttonTrailingMarginConstraint.constant = 0
self.view.layoutIfNeeded() // Necessary when changing constraints.
}
)
Final result:
Git clone project: https://github.com/backslash-f/animating-constraints
Possible a duplicate, but I tried could not make it work so came here.
I (new to autolayout) have two UILabel place one below each other with fixed height space.Both can increase with as per text with in it.
When First UILabel hides bottom should move to First place. How to do it using constraints in view only? I know how to do by creating IBOutlet connection of constraints for second UILabel.
EDIT:
Given question is about more about content hugging related, where as my question is add constrains to move to first UILabel position when first hides.
The only way to achieve what you want with AutoLayout is to set constant of all related constraints to zero. And remember NEVER try to set frame or bounds of your view if you are using AutoLayout(unless you override layoutSubviews and do the stuff in that method, which you rarely need to).
You can check out this tiny project: https://github.com/neevek/UIView-Visibility, I bet that is what you want :-)
I want to set a position of an object programmatically. For this I do:
youtubeIco.frame.origin.y = 100
but nothing. It doesn't change coordinates at all. What is the problem?
And a second question:
when I set in the XCode constraints of webView(for ex: 10(top, left, right, bottom) it disappears. But when I do not set constraints for it, it appears. What is the problem?
I want like this:
You need to set full frame like,
youtubeIco.frame = CGRectMake(youtubeIco.frame.origin.x, 100,youtubeIco.frame.size.width,youtubeIco.frame.size.height)
Only y-axis change will not do anything to it, you have to provide full frame for that. or set centre, or offset.
With AutoLayout you can't change a view's frame directly. The constraint puts the object right back where it was.
Instead, you should add constraints that position your views and add IBOutlets to those constraints. Then you use the outlet(s) to change the constant value of the constraint, and the constraint moves your view objects for you. (In this context anything you can display on the screen is a view {a descendant of UIView} including buttons.)
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.