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!
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."
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];
}
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
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
}];
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
}
}