Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto Layout is preventing me from changing a view's center

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?

like image 943
Dan Morrow Avatar asked Dec 07 '22 07:12

Dan Morrow


2 Answers

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.

like image 166
Fogmeister Avatar answered Jan 19 '23 02:01

Fogmeister


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.

like image 45
Simon Avatar answered Jan 19 '23 02:01

Simon