当键盘出现时,UICollectionView的contentOffset会发生变化

6

我正在尝试在我的应用程序中实现“下拉搜索”功能。为了实现这一点,我只是修改了EGOTableViewPullRefresh的一点内容,它的效果很好,除了一个问题。

问题

当用户打开应用程序时,应该出现以下屏幕。最初的UICollectionViewcontentoffset应该是(0,0) enter image description here

如果用户下拉集合视图,应该出现以下屏幕,此时UICollectionViewcontentoffset应该是(0,-60) enter image description here

用户可以通过在上面的屏幕中输入文本来进行搜索。我的问题出现在此屏幕上,因为只要用户点击UITextField以输入文本,UICollectionViewcontentoffset就会从(0,-60)更改为(0,-110),UI看起来像下面的屏幕。我不确定为什么会发生这种变化,请您指导我解决这个问题。

enter image description here


展示一下视图层级结构?你是否使用了 cocoacontrols.com 上的 TPKeyboardAvoidingScrollView 类? - NeverHopeless
那么没有美观且简单的解决方案来处理这个问题吗? - Zaporozhchenko Oleksandr
5个回答

2
我遇到了同样的问题。按照文档中所说,覆盖viewWillAppear:viewDidLoad:和其他方法都没有效果,TPKeyboardAvoidingScrollView也无法解决。在与集合视图苦斗了两天后,我想出了一个非常糟糕的解决办法。这个想法是在键盘即将出现时锁定滚动,这样你的集合视图就不会移动,等键盘结束动画后再解锁滚动。为了做到这一点,你应该子类化UICollectionView以添加一个锁定滚动的标志,订阅键盘通知并正确设置此标志。
在你实施这个解决方案之前,我必须警告你,这是一个非常糟糕的想法,在这之前你应该尝试其他所有可能的方法。然而,这确实有效...
所以你需要这样做:
  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
    }
    

稍后您将处理通知

  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];
        });
    }
    

注意!这里对keyboardDidShow:中的dispatch_after进行一点澄清: 如果你在键盘出现后立即解锁滚动,锁定将被忽略,集合视图将向上滚动。将其包装在dispatch_after中,在0.3秒后运行此代码,它将正常工作。这可能与运行循环有关,应该在真实设备上进行测试,而不是在模拟器中。


如果键盘已经在屏幕上,再次点击UITextfield是不起作用的。 - Zaporozhchenko Oleksandr
我认为不覆盖 - (void)setContentOffset:(CGPoint)contentOffset 方法可能更合适,而应该覆盖另一个方法: -(void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated - Sound Blaster
就我所知 - 如果您按照苹果的示例直接使用键盘避让代码,就会出现这种行为。在键盘出现时,我必须修改它以考虑表视图的偏移量。 - David U

1

问题解决!将我的UICollectionViewController更改为UIViewController,并将我的UICollectionView设置为ViewController.view的子视图。


由于某种原因,UICollectionViewController将一个滚动视图添加为层次结构中的顶部视图。这是键盘出现时被修改的视图。 - r0d0
它对我不起作用... 我在使用带有UICollectionView的UIViewController时遇到了这个问题。 - Radek Wilczak

1

我在UICollectionView中嵌套了一个UITableView,导致滚动出现问题。受@AnkH的启发,我重写了我的UICollectionView,并将以下内容添加到构造函数中:

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)

同时覆盖contentOffset,并在键盘可见时阻止所有contentOffset的修改。完成。 问题是UITableView已经在自身上触发了正确的contentOffset设置,但是然后父UICollectionView添加了自己的contentOffset,导致灾难性的行为。一开始它向上滚动太远,然后向下滚动太远。现在它完美地工作了!


0

这也不是一个优美的解决方案来处理这个问题

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
    }
}

0
最简单的替代方案是使用UIViewController并添加自己的UICollectionView。禁用所有不需要的键盘行为所需的疯狂hack数量非常令人无语。

它对我不起作用... 我在使用带有UICollectionView的UIViewController时遇到了这个问题。 - Radek Wilczak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接