UIView 块动画过渡以及显示/隐藏键盘上的动画内容
在我的应用程序中,我在某些视图上有一个文本字段,当它显示时,它被键盘覆盖。所以我必须滚动视图(甚至重新排列子视图)。为此,我:
注册键盘通知:
[[NSNotificationCenter defaultCenter] addObserver:self 选择器:@selector(moveViewUp) 名称:UIKeyboardWillShowNotification 对象:无]; [[NSNotificationCenter defaultCenter] addObserver:self 选择器:@selector(moveViewDown) 名称:UIKeyboardWillHideNotification 对象:无];
收到通知后,使用块动画移动视图,如下所示:
- (void)moveViewUp { void (^animations)(void) = nil; oldViewFrame = self.view.frame; 动画=^{ CGRect newViewFrame = oldViewFrame; newViewFrame.origin.y -= kViewOffset; self.view.frame = newViewFrame; }; [UIView animateWithDuration:1.0 动画:动画]; } - (void)moveViewDown { void (^animations)(void) = nil; 动画=^{ self.view.frame = oldViewFrame; }; [UIView animateWithDuration:1.0 动画:动画]; }
这工作正常,视图上下滚动,直到我添加更多动画。具体来说,当用户点击按钮时,我将添加到下一个视图的转换:
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
现在我们遇到了问题。
如果点击按钮时第一个视图发生了移动(这意味着键盘可见),则在键盘向下滑动时会同时开始转换到下一个视图,但视图本身不会向下移动,所以在一瞬间我们实际上可以看到底层视图。那是不对的。当我以模态方式呈现下一个视图时(请参阅注释行),所有动画都会按照我想要的方式进行:即键盘隐藏,视图从右侧翻转并向下滚动 - 全部同时进行时间。这很好,但问题是我实际上没有该视图的 UIViewController 。事实上,我正在尝试在没有 UIViewController
的情况下模拟模态行为(为什么会这样?也许这只是一个糟糕的设计,我会就此发布另一个问题)。
那么为什么在这种情况下,来自 moveViewDown
方法的动画没有在适当的时间触发呢?
更新 1
我向每个函数添加了一个调试打印来检查调用顺序,这就是我得到的:
-[KeyboardAnimationViewController moveViewUp]
__-[KeyboardAnimationViewController moveViewUp]_block_invoke_1 <-- scroll up animation
-[KeyboardAnimationViewController switchToNextView:]
-[KeyboardAnimationViewController moveViewDown]
__-[KeyboardAnimationViewController moveViewDown]_block_invoke_1 <-- scroll down animation
即使我在像这样的转换之前显式地将视图向下移动,
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
NSLog(@"%s", __PRETTY_FUNCTION__);
if (self.view.frame.origin.x < 0)
[self moveViewDown];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
我也会得到完全相同相同的日志。
更新 2
我已经进行了更多实验并得出以下结论:
- 如果我显式调用
moveViewDown
或resignFirstResponder:
,则动画将推迟到当前运行循环结束时,当所有待处理的动画实际上开始播放。虽然动画块立即记录到控制台 - 对我来说似乎很奇怪! - 方法
transitionFromView:toView:duration:options:completion:
(也许transitionWithView:duration :options:animations:completion:
也没有检查这个)显然制作了“从视图”和“到视图”的快照,并仅使用这些快照创建动画。由于视图的滚动被推迟,因此快照是在视图仍然偏移时进行的。该方法以某种方式甚至忽略UIViewAnimationOptionAllowAnimatedContent
选项。 - 我设法使用任何 animateWithDuration: ...completion: 方法获得所需的效果。这些方法似乎忽略过渡选项,例如
UIViewAnimationOptionTransitionFlipFromRight
。 - 当调用
removeFromSuperview
时,键盘开始(隐式)隐藏并发送相应的通知。
如果我有错的地方请纠正我。
In my app I have a text field on some view which is covered by the keyboard when it shows up. So I have to scroll the view (or even rearrange the subviews). To do this I:
register for keyboard notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moveViewUp) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moveViewDown) name:UIKeyboardWillHideNotification object:nil];
upon receiving a notification, move the view using block animations like this:
- (void)moveViewUp { void (^animations)(void) = nil; oldViewFrame = self.view.frame; animations = ^{ CGRect newViewFrame = oldViewFrame; newViewFrame.origin.y -= kViewOffset; self.view.frame = newViewFrame; }; [UIView animateWithDuration:1.0 animations:animations]; } - (void)moveViewDown { void (^animations)(void) = nil; animations = ^{ self.view.frame = oldViewFrame; }; [UIView animateWithDuration:1.0 animations:animations]; }
This works fine, the view scrolls up and down, until I add some more animation. Specifically I'm adding a transition to a next view when the user taps a button:
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
Now we got to the problem.
If the first view was shifted when the button was tapped (that means that the keyboard was visible), the transition to the next view starts simultaneously as the keyboard slides down, but the view itself doesn't move down, so for a split second we can actually see the underlying view. That's not right. When I present the next view modally (see the commented line) all animations go as I want them to: i.e. the keyboard is hiding, the view is flipping from right and scrolling down -- all at the same time. This would be fine, but the problem is that I actually don't have a UIViewController
for that view. In fact I'm trying to simulate the modal behavior without UIViewController
(why so? perhaps it's just a bad design, I'll post another question on that).
So why does in this case the animation from moveViewDown
method is not triggered at the proper time?
Update 1
I added a debug print to each function to check the order of calling, this is what I get:
-[KeyboardAnimationViewController moveViewUp]
__-[KeyboardAnimationViewController moveViewUp]_block_invoke_1 <-- scroll up animation
-[KeyboardAnimationViewController switchToNextView:]
-[KeyboardAnimationViewController moveViewDown]
__-[KeyboardAnimationViewController moveViewDown]_block_invoke_1 <-- scroll down animation
Even if I explicitly move the view down before the transition like this
- (IBAction)switchToNextView:(id)sender {
// [self presentModalViewController:nextViewController animated:YES];
NSLog(@"%s", __PRETTY_FUNCTION__);
if (self.view.frame.origin.x < 0)
[self moveViewDown];
[UIView transitionFromView:self.view
toView:self.nextView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromRight
completion:nil];
}
I get exactly the same log.
Update 2
I've experimented some more and made following conclusions:
- If I call
moveViewDown
orresignFirstResponder:
explicitly, the animation is postponed until the end of current run loop, when all pending animations actually start to play. Though the animation block logs to the console immediately -- seems strange to me! - The method
transitionFromView:toView:duration:options:completion:
(perhapstransitionWithView:duration:options:animations:completion:
too, didn't check this one) apparently makes a snapshot of the "from-view" and the "to-view" and creates an animation using these snapshots solely. Since the scrolling of the view is postponed, the snapshot is made when the view is still offset. The method somehow disregards even theUIViewAnimationOptionAllowAnimatedContent
option. - I managed to get the desired effect using any of
animateWithDuration: ... completion:
methods. These methods seems to disregard transition options likeUIViewAnimationOptionTransitionFlipFromRight
. - The keyboard starts hiding (implicitly) and sends corresponding notification when
removeFromSuperview
is called.
Please correct me if I'm wrong somewhere.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您尝试在没有
UIViewController
的情况下模拟模态行为,我想您希望下一个视图从屏幕底部显示,对吧?如果我错了请纠正我。如果您想要这样的动画,您可以尝试一种解决方法,在
animation
块中更改下一个视图的框架,使其看起来类似于presentModalViewController
If you are trying to simulate the modal behavior without
UIViewController
I guess you want your next view to show up from the bottom of the screen right?. correct me if I am wrong.If you want such an animation you can try a work-around where you change the frame of the next view within an
animation
block such that it appears as if its similar topresentModalViewController