Is there an iOS method that fires when Autolayout has completed?
Asked Answered
B

6

17

I have an iOS app in which I need to know when a new view is completely visible on-screen; that is, when Autolayout has finished its calculations and the view has finished drawing.

ViewDidAppear seems to fire well before the view is completely visible. If I turn off Autolayout, the timing seems to line up as far as human perception goes, but I need to use Autolayout in this project (so this isn't a solution...just a test).

Is there any method that fires when Autolayout is done calculating? Or another method that fires when the view is ACTUALLY visible (since ViewDidAppear doesn't work for this)?

Thanks!

Beget answered 9/5, 2014 at 1:32 Comment(1)
How about viewDidLayoutSubviews?Barvick
O
5

I'm using viewDidLayoutSubviews for this. Apple's documentation says, "Called to notify the view controller that its view has just laid out its subviews."

Outbid answered 9/5, 2014 at 4:22 Comment(2)
... However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted.Similarity
Some sub view layoutSubviews still keeps calling even when the view controller containing it calls viewDidLayoutSubviews. Subviews should not be tightly coupledDogface
G
20

The following can be used to avoid multiple calls:

- (void) didFinishAutoLayout {

    // Do some stuff here.    

    NSLog(@"didFinishAutoLayout");
}

and

- (void) viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];


    [NSObject cancelPreviousPerformRequestsWithTarget:self
                                 selector:@selector(didFinishAutoLayout)
                                           object:nil];
    [self performSelector:@selector(didFinishAutoLayout) withObject:nil
               afterDelay:0];
}
Grimy answered 15/10, 2014 at 4:49 Comment(3)
Although it seems like a hack and there should be a better way... it works and I am at a loss to find any other alternative! The reason I need it is because I need to tell when a particular subview has finished laying out so that I can use it's dimensions, but as far as I can tell, there's no way.Whiffle
Although it's a workaround, I think it's very very neat. Good work!Oleic
I'm just worried about the delay. It might break down if device will be stressed outOleic
O
5

I'm using viewDidLayoutSubviews for this. Apple's documentation says, "Called to notify the view controller that its view has just laid out its subviews."

Outbid answered 9/5, 2014 at 4:22 Comment(2)
... However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted.Similarity
Some sub view layoutSubviews still keeps calling even when the view controller containing it calls viewDidLayoutSubviews. Subviews should not be tightly coupledDogface
U
2

You might face this problem not just with UIViewControllers but also UIViews. If you have a subview and want to know if AutoLayout has updated it's bounds, here is the Swift 5 implementation,

var viewBounds: CGFloat = 0.0
var autoLayoutHasCompleted: Bool = false

override func awakeFromNib() {
    super.awakeFromNib()

    // someSubView is the name of a view you want to check has changed
    viewBounds = someSubView.bounds.width 
}    

override func layoutSubviews() {
    if viewBounds != someSubView.bounds.width && !autoLayoutHasCompleted {
        // Place your code here
        autoLayoutHasCompleted = true
    }
}
Unreal answered 14/2, 2020 at 1:36 Comment(0)
M
2

If you watched 2018's WWDC about "High-Performance AutoLayout", you would know the answer to this question.

Technically, there is no such API method that will be called when autolayout has completed your view's layout. But when autolayout has completed the calculations, your view's setBounds and setCenter will be called so that your view gets its size and position.

After this, your view's layoutSubviews will be called. So, layoutSubviews can, to some degree, be thought of as the method that fires after autolayout has done calculations.

As to view controller's viewDidLayoutSubviews, this is a bit complicated. The documentation says:

When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted. Each subview is responsible for adjusting its own layout.

So when viewDidLayoutSubviews called on a view controller, only the view controller'view 's first-level subviews are guaranteed to be laid out correctly.

Mroz answered 11/3, 2020 at 1:22 Comment(0)
V
0

What it worked in my case was request layout after changed a constraint value:

    self.cnsTableviewHeight.constant = 50;
    [self layoutIfNeeded];

Later on override layoutSubviews method:

    - (void) layoutSubviews { //This method when auto layout engine finishes
    }

You can call setNeedsLayout also instead of layoutIfNeeded

Vital answered 28/6, 2016 at 15:15 Comment(0)
H
0

I guess implementing viewDidLayoutSubviews is the correct way but I used an animation just to write the completion callback inside the same method.

someConstraint.constant = 100; // the change

// Animate just to make sure the constraint change is fully applied
[UIView animateWithDuration:0.1f animations:^{
    [self.view setNeedsLayout];
} completion:^(BOOL finished) {
    // Here do whatever you need to do after constraint change
}];
Hurley answered 19/9, 2017 at 16:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.