UIPageViewController
的转场动画之外更快地导航时,我会收到“'Unbalanced calls to begin/end appearance transitions for <MyDataViewController>'” 的错误提示,并且其中一个视图在横向模式下不会显示,直到我尝试翻页为止。有人有解决这个错误的想法吗?
UIPageViewController
的转场动画之外更快地导航时,我会收到“'Unbalanced calls to begin/end appearance transitions for <MyDataViewController>'” 的错误提示,并且其中一个视图在横向模式下不会显示,直到我尝试翻页为止。以上的回答是正确的,但我认为比必要的更详细,而且食谱很有帮助。所以这是对我有效的东西:
在设置并调用pageViewController的视图控制器中,声明:
@property (assign) BOOL pageIsAnimating;
在viewDidLoad中:
pageIsAnimating = NO;
添加这个:- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
pageIsAnimating = YES;
}
并且添加几行到:
- (void)pageViewController:(UIPageViewController *)pageViewController
didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers
transitionCompleted:(BOOL)completed {
if (completed || finished) // Turn is either finished or aborted
pageIsAnimating = NO;
...
}
手势被抑制,通过拒绝提供视图控制器信息来实现: 手势被抑制,通过拒绝提供视图控制器信息来实现:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController {
if (pageIsAnimating)
return nil;
...
return after;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController {
if (pageIsAnimating)
return nil;
...
return before;
}
哦,而且方向更改会重置该标志:
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation {
pageIsAnimating = NO;
...
}
按照以下步骤解决:
1- 声明一个标志来指示动画是否已经完成:
BOOL pageAnimationFinished;
2- 在viewDidLoad中将此标志设置为true:
pageAnimationFinished = YES;
3- 禁用pageViewController的tapGesture,并将panGestureRecognizer委托给“self”:
for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
if ([gesRecog isKindOfClass:[UITapGestureRecognizer class]])
gesRecog.enabled = NO;
else if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]])
gesRecog.delegate = self;
}
4- 通过以下手势识别器委托方法允许/禁止panGestureRecognizer:
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ([gestureRecognizer.view isEqual:self.view] || [gestureRecognizer.view isEqual:self.pageViewController.view]))
{
UIPanGestureRecognizer * panGes = (UIPanGestureRecognizer *)gestureRecognizer;
if(!pageAnimationFinished || (currentPage < minimumPage && [panGes velocityInView:self.view].x < 0) || (currentPage > maximumPage && [panGes velocityInView:self.view].x > 0))
return NO;
pageAnimationFinished = NO;
}
return YES;
}
5- 添加以下pageViewController委托方法:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
pageAnimationFinished = YES;
}
Basem Saadawy的回答很好,但它有一些缺陷。
实际上,委托的gestureRecognizerShouldBegin:可能会在没有进一步动画开始的情况下调用。如果你通过垂直手指移动来启动手势,并且它的水平偏移量不足以启动动画(但足以启动gestureRecognizerShouldBegin:),那么我们的变量pageAnimationFinished将被设置为 NO ,没有真正的动画。因此,pageViewController:didFinishAnimating:永远不会被调用,你将得到当前页面被冻结,无法更改。
这就是为什么将 NO 分配给此变量的更好位置是手势识别器的操作方法,并检查其速度和平移(我们只关心水平方向)。
因此,最后的步骤是:
1)声明一个实例变量(标志):
BOOL pageAnimationFinished;
2) 设置其初始值
- (void)viewDidLoad
{
[super viewDidLoad];
...
pageAnimationFinished = YES;
}
3) 为平移手势识别器指定委托和自定义操作
for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
if ([gesRecog isKindOfClass:[UIPanGestureRecognizer class]])
{
gesRecog.delegate = self;
[gr addTarget:self action:@selector(handlePan:)];
}
}
3') 当手势在水平方向上的位移较大且手指正在水平移动时,动画才真正开始。
我猜测内部识别器由 UIPageViewController 分配的操作也是使用相同的逻辑。
- (void) handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
if (pageAnimationFinished && gestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint vel = [gestureRecognizer velocityInView:self.view];
CGPoint tr = [gestureRecognizer translationInView:self.view];
if (ABS(vel.x) > ABS(vel.y) && ABS(tr.x) > ABS(tr.y))
pageAnimationFinished = NO; // correct place
}
}
4) 如果动画未完成,则禁止手势。
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && ([gestureRecognizer.view isEqual:self.view] || [gestureRecognizer.view isEqual:self.pageViewController.view]))
{
UIPanGestureRecognizer * panGes = (UIPanGestureRecognizer *)gestureRecognizer;
if(!pageAnimationFinished || (currentPage < minimumPage && [panGes velocityInView:self.view].x < 0) || (currentPage > maximumPage && [panGes velocityInView:self.view].x > 0))
return NO;
}
return YES;
}
5) 动画已完成
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
pageAnimationFinished = YES;
}
添加以下代码(确保在头文件或类扩展中包含UIPageViewControllerDelegate,并将self.pageViewController.delegate = self;
赋值):
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers {
self.pageAnimationFinished = NO;
}
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
self.pageAnimationFinished = YES;
}
然后检查self.pageAnimationFinished
,如果它等于NO
,则返回nil。
更详细的解释:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
UIPageViewControllerDelegate
中的这个委托方法来知道翻转或滑动页面的动画何时结束。我们只需要像这样实现即可:- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
pageAnimationFinished = YES;
}
nil
即可。- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(PageViewController *)viewController
并且
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(PageViewController *)viewController
当 pageAnimationFinished == NO
时,一定要在动画时将 pageAnimationFinished
设置为 NO
。最好的方法是使用相反的方式来确定何时进行动画。
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
即:
- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
自从那时候以来,我再也没有看到过这个警告,而且这可以用其他解决方案的三分之一行数完成。而且这个方法更容易理解。
var pageIsAnimating = false
设置动画状态:
func pageViewController(pageViewController: UIPageViewController, willTransitionToViewControllers pendingViewControllers: [UIViewController]) {
self.pageIsAnimating = true
}
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if finished || completed {
self.pageIsAnimating = false
}
}
如果当前正在进行动画,请阻止过渡效果:
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
if self.pageIsAnimating {
return nil
}
// Your code here
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
if self.pageIsAnimating {
return nil
}
// Your code here
}
我的解决方案是用Swift编写的,简单易懂且有效:
Add below code
extension MyPageVC: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
self.view.isUserInteractionEnabled = false
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
self.view.isUserInteractionEnabled = true
}
}
- (void)pageViewController:(UIPageViewController*)pgVC willTransitionToViewControllers:(NSArray*)pendingVCs
{
pgVC.dataSource = nil; // ... to disallow user to change pages until animation completes
}
- (void)pageViewController:(UIPageViewController*)pgVC
didFinishAnimating:(BOOL)finished
previousViewControllers:(NSArray*)prevVCs
transitionCompleted:(BOOL)completed
{
if(completed || finished) {
pgVC.dataSource = _pgViewDataSource; // ... to allow user to change pages again
}
}
我不得不将它添加到viewDidAppear:中才能使其工作
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.pageAnimationFinished = YES;
}
利用您的UIPageViewControllerDelegate方法并设置保护措施,以防止检测到过多页面翻转时创建新的页面视图。
我会尝试在转场时忽略 UIPageViewController 上的手势。
UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
方法。 - Sugan S