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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With