I have a really interesting issue with UIPageViewController.
My project is set up very similarly to the example Page Based Application template. Every now and then (
Since I was still receiving mysterious crashes with the implementation in my first answer, I kept searching for a "good enough" solution which depends less on personal assumptions about the page view controller's (PVC) underlying behavior. Here is what I managed to come up with.
My former approach was kind of intrusive and was more of a workaround than an acceptable solution. Instead of fighting the PVC to force it to do what I thought it was supposed to do, it seems that it's better accept the facts that:
pageViewController:viewControllerBeforeViewController: and pageViewController:viewControllerAfterViewController: methods can be called an arbitrary number of times by UIKit, andpageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:That means we cannot use the before/after methods as "animation-begin" (note, however, that didFinishAnimating still serves as "animation-end" event). So how do we know an animation has indeed started?
Depending on our needs, we may be interested in the following events:
the user begins fiddling with the page: A good indicator for this is the before/after callbacks, or more precisely the first of them.
first visual feedback of the page turning gesture:
We can use KVO on the state property of the tap and pan gesture recognizers of the PVC. When a UIGestureRecognizerStateBegan value is observed for panning, we can be pretty sure that visual feedback will follow.
the user finishes dragging the page around by releasing the touch:
Again, KVO. When the UIGestureRecognizerStateRecognized value is reported either for panning or tapping, it is when the PVC is actually going to turn the page, so this may be used as "animation-begin".
UIKit starts the paging animation: I have no idea how to get a direct feedback for this.
UIKit concludes the paging animation:
Piece of cake, just listen to pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:.
For KVO, just grab the gesture recognizers of the PVC as below:
@interface MyClass ()
{
UIPanGestureRecognizer* pvcPanGestureRecognizer;
UITapGestureRecognizer* pvcTapGestureRecognizer;
}
...
for ( UIGestureRecognizer* recognizer in pageViewController.gestureRecognizers )
{
if ( [recognizer isKindOfClass:[UIPanGestureRecognizer class]] )
{
pvcPanGestureRecognizer = (UIPanGestureRecognizer*)recognizer;
}
else if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] )
{
pvcTapGestureRecognizer = (UITapGestureRecognizer*)recognizer;
}
}
Then register your class as observer for the state property:
[pvcPanGestureRecognizer addObserver:self
forKeyPath:@"state"
options:NSKeyValueObservingOptionNew
context:NULL];
[pvcTapGestureRecognizer addObserver:self
forKeyPath:@"state"
options:NSKeyValueObservingOptionNew
context:NULL];
And implement the usual callback:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ( [keyPath isEqualToString:@"state"] && (object == pvcPanGestureRecognizer || object == pvcTapGestureRecognizer) )
{
UIGestureRecognizerState state = [[change objectForKey:NSKeyValueChangeNewKey] intValue];
switch (state)
{
case UIGestureRecognizerStateBegan:
// trigger for visual feedback
break;
case UIGestureRecognizerStateRecognized:
// trigger for animation-begin
break;
// ...
}
}
}
When you are done, don't forget to unsubscribe from those notifications, otherwise you may get leaks and strange crashes in your app:
[pvcPanGestureRecognizer removeObserver:self
forKeyPath:@"state"];
[pvcTapGestureRecognizer removeObserver:self
forKeyPath:@"state"];
That's all folks!