iOS6のメソッドviewControllerAfterViewControllerおよびviewControllerBeforeViewControllerでnilを返すと(最初または最後のページにいるときにページナビゲーションをブロックするため)、この例外を除いてアプリがクラッシュします。
'提供されたビューコントローラーの数(0)が、要求された遷移に必要な数(1)と一致しません'
iOS5ではすべてがうまく機能します。
iOS6のメソッドviewControllerAfterViewControllerおよびviewControllerBeforeViewControllerでnilを返すと(最初または最後のページにいるときにページナビゲーションをブロックするため)、この例外を除いてアプリがクラッシュします。
'提供されたビューコントローラーの数(0)が、要求された遷移に必要な数(1)と一致しません'
iOS5ではすべてがうまく機能します。
同じ問題がありました。原因は、UIPageViewController の UIPanGestureRecognizer のデリゲートを置き換えることであることがわかりました。パン ジェスチャ レコグナイザーは、文書化されていないメソッド _gestureRecognizerShouldBegin: (先頭のアンダースコアに注意) を呼び出していました。これは、UIPageViewController が実装し、適切に動作するために依存しているようです (読み取り: クラッシュしない)。UIPageViewController を使用して文書化されていないデリゲート メソッドを UIPageViewController に渡すクラスに、特に名前を付けずに RespondsToSelector: と forwardingTargetForSelector: を実装することになりました (ほぼ確実にアプリ ストアのレビューで拒否されました)。
-(BOOL)respondsToSelector:(SEL)aSelector {
if ([super respondsToSelector:aSelector])
return YES;
else if ([self.pageViewController respondsToSelector:aSelector])
return YES;
else
return NO;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([super respondsToSelector:aSelector]) {
return nil;
} else if ([self.pageViewController respondsToSelector:aSelector]) {
return self.pageViewController;
}
return nil;
}
私の長期的な解決策は、ジェスチャー認識デリゲートを置き換える必要がないように、UIPageViewController の使用を作り直すことです。
ああ、どうして誰もこのバグを指摘しないのだろうと思っていたのですが、解決策を見つけるのにほぼ 2 晩かかりました。
古いコード (iOS 5.1) : 最初と最後のページで nil を返すと、アプリがクラッシュします。iOS 5.1 では正常に動作しますが、iOS 6 では動作しません。
- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerBeforeViewController:
(UIViewController *)viewController
{
for (UIGestureRecognizer *recognizer in pageController.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
recognizer.enabled = NO;
}
}
NSUInteger index = [self indexOfViewController:
(MainViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
for (UIGestureRecognizer *recognizer in pageController.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
recognizer.enabled = NO;
}
}
NSUInteger index = [self indexOfViewController:
(MainViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
}
解決策 (iOS 6) : ジェスチャー効果をスーパービューに追加した後、-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer というデリゲートを呼び出すだけです。私がしたことは静かでシンプルで、ユーザーが最初のページと最後のページをめくる速度を計算します (つまり、ジェスチャ認識機能を使用することを意味します)。スワイプを拒否しました。次のコードを貼り付けるだけで完了です。 !.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (pageNum==0) {
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x > 0.0f) {
//NSLog(@"Swiping to left on 1st page is denied");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x < self.view.frame.size.width/2) {
//NSLog(@"tapping to left on 1st page is denied");
return NO;
}
}
else if(pageNum ==totalNoOfFiles-1)
{
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x < 0.0f) {
//NSLog(@"Swiping to right on 1st page is denied");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x > self.view.frame.size.width/2) {
//NSLog(@"Tapping to right on 1st page is denied");
return NO;
}
}
return YES;
}
- (UIViewController *)pageViewController:(UIPageViewController*) pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
int index = [self indexOfViewController:(ChildViewController *)viewController];
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:
(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
int index = [self indexOfViewController:(ChildViewController *)viewController];
index++;
return [self viewControllerAtIndex:index];
}
これについてはよく議論されていますが、追加することが1つあります。ジェスチャ レコグナイザのデリゲートを自分自身に設定した理由を考えてみましょう。私の場合、場合によっては、デリゲートのgestureRecognizerShouldBegin:
.
gestureRecognizerShouldBegin:
しかし、この問題が発生する iOS 6 では、UIViewに実装することで、まったく新しい方法でそれを行うことができます。(これは、iOS 6 の新しい UIView インスタンス メソッドです。)
したがって、ジェスチャ認識エンジンのデリゲートを変更することなく、以前に達成していたことを正確に達成することができました。
同じエラーで iOS6 で UIPageViewController がクラッシュするという問題がありました (「提供されたビュー コントローラーの数 (0) は、要求された遷移に必要な数 (1) と一致しません」)。
上記の解決策はどれもうまくいきませんでしたが、最終的に次の行を移動しviewDidLoad
てviewDidAppear
修正したことがわかりました。
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
ここでまったく同じ問題。
私がしたことは、nil の代わりに before/afterViewController のクローンを返すだけのホットフィックスでした。
// viewController = before/afterViewController
NSUInteger index = [self indexOfViewController:viewController];
// NOTE: return nil crashes in iOS6
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
これは、永遠にページをカールできることを意味しますが、他に選択肢はありませんでした...より良い解決策はいつでも歓迎されます。