UICollectionView contentOffset changes when keyboard appears
Asked Answered
F

5

6

I am trying to implement PullDown To Search feature in my app. To implement this i just modified bit EGOTableViewPullRefresh it is working good except an issue.

Issue

When user opens the application following screen shall appear. initially the UICollectionView's contentoffset shall be (0,0) enter image description here

If user pulls down the collection view the following screen shall appear at this point the contentoffset of UICollectionView shall be (0,-60) enter image description here

User can search by entering their text in the above screen. My issue is occurring in this screen as soon as user taps the UITextField to enter text contentoffset of UICollectionView changes (0,-60) to (0,-110) and UI loosks like bellow screen. I am not sure why this change occurs can you please guide me to resolve this?

enter image description here

Freese answered 17/6, 2014 at 6:6 Comment(2)
Show us the view hierarchy? Did you used TPKeyboardAvoidingScrollView class from cocoacontrols.com ?Nudicaul
So there isn't beautiful and easy decision to handle this?Wellbred
F
2

Had the same issue. Overriding viewWillAppear:, viewDidLoad: and other methods as told in documentation had no effect, TPKeyboardAvoidingScrollView didn't help either. After struggling with collection view for over 2 days I've come to a really bad workaround. The idea is to lock scrolling up when keyboard is about to appear, so your collection view will not move anywhere, and unlock scroll after keyboard ends animation. To do so you should subclass UICollectionView to add a flag that locks scroll, subscribe to keyboard notifications and set this flag properly.

Before you implement this workaround, I must warn you that this is a VERY bad idea, and you should try everything else before doing that. Still, this works...

So here is what you do:

  1. In viewWillAppear of your viewController subscribe for keyboard notifications:

    - (void)viewWillAppear:(BOOL)animated
    {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardDidShow:)
                                                     name:UIKeyboardDidShowNotification
                                                   object:nil];
        // you custom code here
    }
    

You will handle notifications later

  1. Don't forget to unsubscribe from notifications:

    - (void)viewWillDisappear:(BOOL)animated
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        // your custom code
    }
    
  2. Subclass UICollectionView and add flag:

    @property (nonatomic, getter=isLocked) BOOL locked;
    
  3. Override setContentOffset: in your collection view:

    - (void)setContentOffset:(CGPoint)contentOffset
    {
        if (contentOffset.y < self.contentOffset.y && self.isLocked) // user scrolls up
            contentOffset.y = self.contentOffset.y; // ignore new Y coordinate, so collection view will not scroll up
        [super setContentOffset:contentOffset];
    }
    
  4. In your viewController create methods for keyboard handling to lock and unlock scroll:

    - (void)keyboardWillShow:(NSNotification *)aNotification
    {
        [(LockableCollectionView *)self.collectionView setLocked:YES];
    }
    
    - (void)keyboardDidShow:(NSNotification *)aNotification
    {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [(LockableCollectionView *)self.collectionView setLocked:NO];
        });
    }
    

Warning! Here is a little clarification about dispatch_after in keyboardDidShow: If you unlock scroll right after keyboard did show, locking will be ignored and collection view will scroll up. Wrap it into dispatch_after to run this code after 0.3 seconds and it works well. This is probably related with run loops and should be tested on real devices, not in simulator.

Funds answered 2/4, 2015 at 18:22 Comment(3)
It doesn't really work if u click again on UITextfield, when keyboard is already on the screen.Wellbred
I think may be more right to not override - (void)setContentOffset:(CGPoint)contentOffset and override another method: -(void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animatedPhlegmy
FWIW - I discovered this behavior occurs if you use the keyboard avoidance code in Apple's example as is. I had to modify it to take into account the offset of the table view at the time the keyboard appears.Kalsomine
B
1

problem solve! changing my UICollectionViewController by a UIViewController and make my UICollectionView a subView of my ViewController.view.

Beat answered 20/6, 2017 at 14:13 Comment(2)
for some reason the UICollectionViewController add an scrollview as top view in the hierarchy. That was the view that get modified when keyboard appear.Beat
It doesn't work for me... I'm experience this problem while using UIViewController with UICollectionView.Townswoman
S
1

I had an UITableView inside an UICollectionView and got weird scrolling. Inspired by @AnkH I override my UICollectionView and added this into the constructor:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow),
            name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidHide),
            name: NSNotification.Name.UIKeyboardDidHide, object: nil)

Override contentOffset too and blocked all modifications of contentOffset while the keyboard was visible. Done. The problem is that UITableView already triggers the correct contentOffset setting on itself, but then the parent UICollectionView adds its own contentOffset, resulting in disastrous behaviour. First it was scrolling too far up, then too far down. Now it works flawlessly!

Sopher answered 18/1, 2018 at 10:26 Comment(0)
I
0

also not a beautiful solution to handle this

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: UIKeyboardWillShowNotification, object: nil)

 

func keyboardWillShow()  {
    let offset = self.collectionView!.contentOffset;
    self.collectionView!.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right:0 )
    self.collectionView!.contentOffset = offset

    let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
    dispatch_after(delayTime, dispatch_get_main_queue()) {
        let offset = self.collectionView!.contentOffset;
        self.collectionView!.contentInset = UIEdgeInsets(top: 60, left: 0, bottom: 0, right:0 )
        self.collectionView!.contentOffset = offset
    }
}
Imine answered 10/5, 2016 at 19:26 Comment(0)
N
0

The easiest alternative is to use a UIViewController and add your own UICollectionView. The amount of crazy hacks needed to disable all the undesired behavior of the keyboard is just insane.

Nitwit answered 13/11, 2016 at 22:39 Comment(1)
It doesn't work for me... I'm experience this problem while using UIViewController with UICollectionView.Townswoman

© 2022 - 2024 — McMap. All rights reserved.