What finally worked was to suppress the messages that invalidate the layout from the time the keyboard starts moving until it's completely hidden. The target app is Objective-C but it will be trivial to convert to Swift.
@interface SSCollectionViewFlowLayout : UICollectionViewFlowLayout
@property (nonatomic, assign) BOOL shouldSuppress;
@end
@implementation SSCollectionViewFlowLayout
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardUp)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDown)
name:UIKeyboardWillHideNotification
object:nil];
return self;
}
- (void)keyboardUp {
self.shouldSuppress = YES;
}
- (void)keyboardDown {
self.shouldSuppress = NO;
}
- (void)prepareLayout {
if(self.shouldSuppress) { return; }
[super prepareLayout];
}
- (void)invalidateLayout {
if(self.shouldSuppress) { return; }
[super invalidateLayout];
}
- (void)invalidateLayoutWithContext:(UICollectionViewLayoutInvalidationContext *)context {
if(self.shouldSuppress) { return; }
[super invalidateLayoutWithContext:context];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
Both the invalidate
messages were sent to this object.