Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KVO collection view content inset in iOS9

With the recent release of iOS 9, there may need to be some updates to existing code to compensate for any changes made to apples API. Recently it seems they have made it so that collection views now automatically adjust it's content inset when a keyboard appears. This is useful for people who aren't handling it manually and or supporting multiple OS versions. In my application it's caused a bit of a headache. I finally came up with a solution using KVO to inform me when the system changes the insets and i react accordingly, it all works fine, with the exception of a single edge case.

If i show the keyboard and then attempt to go back in the navigation stack via an interactive swipe causing beginAppearanceTransition:animated: to be called, but then cancel it and then tap out side the keyboard to resign the first responder, the system all of a sudden decides it does not want to automatically update my insets and my KVO never get's triggered for the content inset, the keyboard goes away but the content inset is not reduced causing it too look wrong...if however i tap on the textfield causing the keyboard to show again, all of a sudden it decides to do it's automatic updating again.

Does anyone have anyone have any idea as to why it ignores my first dismissal of the keyboard after cancelling a interactive transition for updating my insets?

EDIT

Having to revisit this as the team feels it is far too fragile and hacky, that and after playing with this to find out how they handle the same case, they do not seem to have to deal with the erroneous call from no where. So i subclasses UICollectionView and overrode the setContentInset Function only to find the offending call here

IMG

Except the stack trace is not particularly helpful at this point, does anyone have any idea?

like image 292
Genhain Avatar asked Nov 10 '22 04:11

Genhain


1 Answers

As no answer seems to be forth coming and other people have run into this problem, as requested here is my current solution.

So after adding an observer for the contentInset I have the following function.

static bool _willSystemUpdateCollectionViewInset = NO;
static bool _willCustomKeyboardViewUpdateCollectionViewInset = NO;
static bool _didCancelDismissInteraction = NO;
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:NSStringFromSelector(@selector(contentInset))])
    {
        id oldNumber = change[@"old"];
        id newNumber = change[@"new"];

        if (_willSystemUpdateCollectionViewInset)
        {
            _willSystemUpdateCollectionViewInset = NO;
            UICollectionView *collectionView = (UICollectionView*)object;
            UIEdgeInsets insets = collectionView.contentInset;
            insets.bottom = [oldNumber UIEdgeInsetsValue].bottom;

            [collectionView setContentInset:insets];
        }
        else if (_willCustomKeyboardViewUpdateCollectionViewInset)
        {
            _willCustomKeyboardViewUpdateCollectionViewInset = NO;
            [self updateScrollViewInsets];
        }

        if ([newNumber UIEdgeInsetsValue].bottom > [oldNumber UIEdgeInsetsValue].bottom )
            [_messageViewController scrollCollectionViewToBottom:NO];
    }
    else
    {
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}

So on the keyboard showing or hiding the flag _willSystemUpdateCollectionViewInset is set to YES, and the above function essentially negates the change that is made by the system automatically.

like image 184
Genhain Avatar answered Nov 14 '22 23:11

Genhain