12

尝试做一些类似于 Messages.app 的行为,我UIScrollView在它下面有一个文本字段,并尝试对其进行动画处理,以便当键盘出现时,使用向上移动字段的约束将所有内容都向上移动到键盘上方(并且由于自动布局,的UIScrollView高度也会发生变化)并且同时将 设置contentOffset为滚动到底部。

代码完成了想要的最终结果,但是在动画期间,当键盘动画开始时,滚动视图变为空白,然后内容从底部向上滚动,而不是从动画开始时的位置滚动。

动画是这样的:

- (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration {
    self.keyboardHeight.constant = -height;
    [self.view setNeedsUpdateConstraints];

    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
        [self.view layoutIfNeeded];
        self.collectionView.contentOffset = 
            CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height);
    } completion:nil];
}

此处提供了该问题的视频。

谢谢!

4

6 回答 6

35

这可能是 UIKit 中的一个错误。当 和 的同时发生变化时,就会发生size这种contentOffset情况UIScrollView。测试这种行为是否也会在没有自动布局的情况下发生会很有趣。

我找到了解决此问题的两种解决方法。

使用 contentInset(消息方法)

正如在消息应用程序中可以看到的那样,UIScrollView当显示键盘时,它的高度不会改变 - 消息在键盘下可见。你可以这样做。UICollectionView删除和包含UITextFieldand的视图之间的约束UIButton(我称之为messageComposeView)。UICollectionView然后在和之间添加约束Bottom Layout Guide。保持 和 之间的messageComposeView约束Bottom Layout Guide。然后使用contentInsetUICollectionView视觉上的最后一个元素保持在键盘上方。我是通过以下方式做到的:

- (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration {
    self.bottomSpaceConstraint.constant = height;

    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
        CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height));
        [self.collectionView setContentOffset:bottomOffset animated:YES];

        [self.collectionView setContentInset:UIEdgeInsetsMake(0, 0, height, 0)];

        [self.view layoutIfNeeded];
    } completion:nil];
}

这是和self.bottomSpaceConstraint之间的约束。这是显示其工作原理的视频。 更新 1:这是我在 GitHub 上的项目源代码。这个项目有点简化。我应该考虑在通知中传递的选项。messageComposeViewBottom Layout Guide - (void)keyboardWillShow:(NSNotification *)notif

在队列中执行更改

不是一个确切的解决方案,但如果将其移动到完成块,滚动效果很好:

} completion:^(BOOL finished) {
    [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height) animated:YES];
}];

键盘显示需要 0.25 秒,因此动画开始之间的差异可能很明显。动画也可以以相反的顺序完成。

更新 2:我还注意到 OP 的代码适用于此更改:

CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height));

但只有当contentSize'sheight小于某个固定值时(在我的情况下800,但我的布局可能有点不同)。

最后,我认为我提出的方法Using contentInset (the Messages approach)比调整大小更好UICollectionView。使用时,contentInset我们还可以看到键盘下的元素。它当然更适合 iOS 7 风格。

于 2013-10-13T10:56:56.413 回答
8

我有一个类似的问题——当动画帧和偏移时,内容会在动画到位之前“跳跃”——而整个解决方案只是添加UIViewAnimationOptionBeginFromCurrentState到动画选项中。瞧!

于 2014-08-06T18:28:38.550 回答
2

尝试删除该[self.view layoutIfNeeded]行并查看问题是否仍然存在或是否出现其他问题,如果是,它们是否看起来有任何关联。

此外,在动画之前重置视图的位置总是一个好主意。因此,请尝试在动画线之前设置法线偏移量(甚至可能在layoutIfNeeded那里调用方法) 在开始动画之前将所有内容按顺序排列。

于 2013-10-13T14:55:37.837 回答
1

我不确定这是否正是您想要的行为,但也许它可以让您朝着正确的方向轻推:Github 项目

我所做的是设置两个约束,一个用于文本字段(到底部指南),另一个用于滚动视图(到文本字段)。

然后在调用动画时,我为两个元素的“中心”属性设置动画,而不是 contentOffset,我分别处理动画值和约束值。

最终结果在这里:

Youtube 视频

于 2013-10-13T14:39:57.317 回答
1

setNeedsUpdateConstraints真的需要吗?自动布局不是自动做的吗?

如果不是 - 我建议您在不使用自动布局的情况下单独完成所有调整大小的动画。看起来问题出在您尝试同时制作两个动画时(但不是在同一时间)

于 2013-10-11T10:51:12.620 回答
0

NSNotifications我有同样的问题,我可以通过注册键盘/隐藏和显示来解决它。我正在提供相同的代码。希望它会帮助你。

只需在 .h 类中声明 BOOL isMovedUp

在 ViewDidLoad

// registering notifications for keyboard/hiding and showing
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];    
}

#pragma mark-keyboard notifications
- (void)keyboardWillShow {
    // Animate the current view out of the way
    if (isMovedUp==YES){

    } else {

        [self setViewMovedUp:YES];
        isMovedUp=YES;
    }
}

- (void)keyboardWillHide {
    if (isMovedUp==YES) {
        [self setViewMovedUp:NO];
        isMovedUp=NO;
    }        
}

//method for view transformation

-(void)setViewMovedUp:(BOOL)movedUp {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5]; // if you want to slide up the view

    CGRect rect = self.winePopUpView.frame;

    if (movedUp) {
        //isKeyBoardDown = NO;
        // 1. move the view's origin up so that the text field that 
        // will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area 
        // behind the keyboard is covered up.
        rect.origin.y -= 100;
        //rect.size.height += 100;
    } else  {
        // revert back to the normal state.
        rect.origin.y += 100;
        //rect.size.height -= 100;
        //isKeyBoardDown = YES;
    }
    self.winePopUpView.frame = rect;
    [UIView commitAnimations];
}
于 2013-10-14T06:25:27.560 回答