UIPageViewController
トランジション アニメーションよりも速く移動するUnbalanced calls to begin/end appearance transitions for <MyDataViewController>
と、「 」が表示され、ページをめくるまで横向きの 2 つのビューの 1 つが表示されません。
誰でもこのバグを解決するアイデアを持っていますか?
UIPageViewController
トランジション アニメーションよりも速く移動するUnbalanced calls to begin/end appearance transitions for <MyDataViewController>
と、「 」が表示され、ページをめくるまで横向きの 2 つのビューの 1 つが表示されません。
誰でもこのバグを解決するアイデアを持っていますか?
上記の答えは正しかったですが、必要以上に手の込んだものだと思います。クックブックは役に立ちます。だからここに私のために働いているように見えるものがあります:
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
== の場合はチェックして nil を返しNO
ます。
より長い説明:
- (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
それ以来、その警告は表示されていません。これは、他のソリューションの 1/3 の行で実行できます。そして、従うのははるかに簡単です。
Bill Cheswickの回答のSwiftバージョンは次のとおりです(現在、トップの回答):
現在の状態を保持する変数を追加します。
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
}
ありがとうビル・チェスウィック!
UIPageViewControllerDelegate メソッドを利用し、ガードを設定して、過剰なページ ターンが検出されたときに新しいページ ビューが作成されないようにします。
viewDidAppear: に追加して機能させる必要がありました
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.pageAnimationFinished = YES;
}
移行中はUIPageViewControllersのジェスチャーを無視しようとします。