Skip to content

Instantly share code, notes, and snippets.

@paul-delange
Last active October 25, 2021 01:29
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paul-delange/5582394 to your computer and use it in GitHub Desktop.
Save paul-delange/5582394 to your computer and use it in GitHub Desktop.
#import <UIKit/UIKit.h>
@interface AlzheimerPageViewController : UIViewController
- (instancetype) initWithTransitionStyle: (UIPageViewControllerTransitionStyle) transitionStyle navigationOrientation:(UIPageViewControllerNavigationOrientation)navigationOrientation options:(NSDictionary *)options;
- (void) setViewControllers: (NSArray*) viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^)(BOOL))completion;
@property (nonatomic, weak) id<UIPageViewControllerDataSource> dataSource;
@property (nonatomic, weak) id<UIPageViewControllerDelegate> delegate;
@property (nonatomic, readonly) NSArray* viewControllers;
@property (nonatomic, readonly) NSArray* gestureRecognizers;
@end
#import "AlzheimerPageViewController.h"
@interface AlzheimerPageViewController () {
UIPageViewControllerTransitionStyle _transitionStyle;
UIPageViewControllerNavigationOrientation _navigationOrientation;
}
@end
@implementation AlzheimerPageViewController
- (instancetype) initWithCoder:(NSCoder *)aDecoder {
return [self initWithTransitionStyle: UIPageViewControllerTransitionStyleScroll
navigationOrientation: UIPageViewControllerNavigationOrientationHorizontal
options: nil];
}
- (instancetype) initWithTransitionStyle: (UIPageViewControllerTransitionStyle) transitionStyle navigationOrientation:(UIPageViewControllerNavigationOrientation)navigationOrientation options:(NSDictionary *)options {
NSParameterAssert(transitionStyle == UIPageViewControllerTransitionStyleScroll);
self = [super init];
if( self ) {
_transitionStyle = transitionStyle;
_navigationOrientation = navigationOrientation;
}
return self;
}
- (void) setViewControllers: (NSArray*) viewControllers direction:(UIPageViewControllerNavigationDirection)direction animated:(BOOL)animated completion:(void (^)(BOOL))completion {
NSParameterAssert(viewControllers.count == 1);
UIViewController* newViewController = viewControllers.lastObject;
NSParameterAssert(self.viewControllers.count <= 1);
UIViewController* oldViewController = self.viewControllers.lastObject;
[newViewController willMoveToParentViewController: self];
[self addChildViewController: newViewController];
[newViewController didMoveToParentViewController: self];
if( self.isViewLoaded ) {
[newViewController beginAppearanceTransition: YES animated: animated];
if( [self.delegate respondsToSelector:@selector(pageViewController:willTransitionToViewControllers:)])
[self.delegate pageViewController: (UIPageViewController*)self
willTransitionToViewControllers: @[newViewController]];
if( oldViewController ) {
CGRect newFrame = self.view.bounds;
if( direction == UIPageViewControllerNavigationDirectionForward ) {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
newFrame.origin.x += CGRectGetWidth(self.view.bounds);
}
else {
newFrame.origin.y -= CGRectGetHeight(self.view.bounds);
}
}
else {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
newFrame.origin.x -= CGRectGetWidth(self.view.bounds);
}
else {
newFrame.origin.y += CGRectGetHeight(self.view.bounds);
}
}
newViewController.view.frame = newFrame;
[self transitionFromViewController: oldViewController
toViewController: newViewController
duration: [self animationDuration] * animated
options: UIViewAnimationOptionCurveEaseOut
animations: ^{
CGRect oldFrame = oldViewController.view.frame;
CGRect newFrame = oldFrame;
if( direction == UIPageViewControllerNavigationDirectionForward ) {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
oldFrame.origin.x -= CGRectGetWidth(self.view.bounds);
}
else {
oldFrame.origin.y += CGRectGetHeight(self.view.bounds);
}
}
else {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
oldFrame.origin.x += CGRectGetWidth(self.view.bounds);
}
else {
oldFrame.origin.y -= CGRectGetHeight(self.view.bounds);
}
}
oldViewController.view.frame = oldFrame;
newViewController.view.frame = newFrame;
} completion:^(BOOL finished) {
[newViewController endAppearanceTransition];
if( [self.delegate respondsToSelector: @selector(pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:)] ) {
[self.delegate pageViewController: (UIPageViewController*)self
didFinishAnimating: YES
previousViewControllers: @[oldViewController]
transitionCompleted: YES];
}
[oldViewController removeFromParentViewController];
}];
}
else {
//First time -> animation is different
newViewController.view.frame = self.view.bounds;
[UIView transitionWithView: self.view
duration: [self animationDuration] * animated
options: UIViewAnimationOptionCurveEaseOut
animations: ^{
[self.view addSubview: newViewController.view];
} completion: ^(BOOL finished) {
[newViewController endAppearanceTransition];
if( [self.delegate respondsToSelector: @selector(pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:)] ) {
[self.delegate pageViewController: (UIPageViewController*)self
didFinishAnimating: YES
previousViewControllers: nil //No previous controller
transitionCompleted: YES];
}
}];
}
}
else {
NSLog(@"%@ set view controllers but the view was not loaded", NSStringFromClass(self.class));
}
}
- (NSArray*) viewControllers {
NSArray* children = self.childViewControllers;
NSParameterAssert(children.count <= 2); //0: empty, 1: static, 2:transitioning
return children;
}
- (NSArray*) gestureRecognizers {
return [self.view.gestureRecognizers filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF isKindOfClass: %@", [UISwipeGestureRecognizer class]]];
}
- (NSTimeInterval) animationDuration {
return [UIApplication sharedApplication].statusBarOrientationAnimationDuration;
}
#pragma mark - UIViewController
- (void) viewDidLoad {
[super viewDidLoad];
UISwipeGestureRecognizer* swipeLeftGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget: self
action: @selector(viewWasSwiped:)];
swipeLeftGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer: swipeLeftGestureRecognizer];
UISwipeGestureRecognizer* swipeRightGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget: self
action: @selector(viewWasSwiped:)];
swipeRightGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer: swipeRightGestureRecognizer];
if( self.viewControllers.count ) {
NSParameterAssert(self.viewControllers.count == 1);
UIViewController* newViewController = self.viewControllers.lastObject;
newViewController.view.frame = self.view.bounds;
[self.view addSubview: newViewController.view];
}
}
#pragma mark - Actions
- (IBAction) viewWasSwiped: (UISwipeGestureRecognizer*)sender {
UIViewController* oldViewController = self.viewControllers.lastObject;
UIViewController* newViewController = nil;
UIPageViewControllerNavigationDirection direction = UIPageViewControllerNavigationDirectionForward;
switch (sender.direction) {
case UISwipeGestureRecognizerDirectionRight:
{
newViewController = [self.dataSource pageViewController: (UIPageViewController*)self
viewControllerBeforeViewController: oldViewController];
direction = UIPageViewControllerNavigationDirectionReverse;
break;
}
case UISwipeGestureRecognizerDirectionLeft:
{
newViewController = [self.dataSource pageViewController: (UIPageViewController*)self
viewControllerAfterViewController: oldViewController];
break;
}
default:
break;
}
if( newViewController ) {
[newViewController willMoveToParentViewController: self];
[self addChildViewController: newViewController];
[newViewController didMoveToParentViewController: self];
[newViewController beginAppearanceTransition: YES animated: YES];
CGRect newFrame = self.view.bounds;
if( direction == UIPageViewControllerNavigationDirectionForward ) {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
newFrame.origin.x += CGRectGetWidth(self.view.bounds);
}
else {
newFrame.origin.y -= CGRectGetHeight(self.view.bounds);
}
}
else {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
newFrame.origin.x -= CGRectGetWidth(self.view.bounds);
}
else {
newFrame.origin.y += CGRectGetHeight(self.view.bounds);
}
}
newViewController.view.frame = newFrame;
newViewController.view.alpha = 0.f;
if( [self.delegate respondsToSelector:@selector(pageViewController:willTransitionToViewControllers:)])
[self.delegate pageViewController: (UIPageViewController*)self
willTransitionToViewControllers: @[newViewController]];
[self transitionFromViewController: oldViewController
toViewController: newViewController
duration: [self animationDuration]
options: UIViewAnimationOptionCurveEaseOut
animations: ^{
CGRect oldFrame = oldViewController.view.frame;
CGRect newFrame = oldFrame;
if( direction == UIPageViewControllerNavigationDirectionForward ) {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
oldFrame.origin.x -= CGRectGetWidth(self.view.bounds);
}
else {
oldFrame.origin.y += CGRectGetHeight(self.view.bounds);
}
}
else {
if( _navigationOrientation == UIPageViewControllerNavigationOrientationHorizontal ) {
oldFrame.origin.x += CGRectGetWidth(self.view.bounds);
}
else {
oldFrame.origin.y -= CGRectGetHeight(self.view.bounds);
}
}
oldViewController.view.frame = oldFrame;
newViewController.view.frame = newFrame;
oldViewController.view.alpha = 0.f;
newViewController.view.alpha = 1.f;
} completion:^(BOOL finished) {
NSParameterAssert(oldViewController.view.superview == nil);
[newViewController endAppearanceTransition];
[oldViewController removeFromParentViewController];
if( [self.delegate respondsToSelector: @selector(pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:)] ) {
[self.delegate pageViewController: (UIPageViewController*)self
didFinishAnimating: YES
previousViewControllers: @[oldViewController]
transitionCompleted: YES];
}
}];
}
}
@end
@yinonar
Copy link

yinonar commented Aug 7, 2015

This is pretty awesome, solves a really annoying bug in the OS.

Question: do you know how to style the UIPageControl when using this? The following (swift) code works for a normal UIPageViewController, if you put it in AppDelegate application/didFinishLaunchingWithOptions function, but does not apply with the AlzheimerPageViewController. Just trying to style the dots.

    let pageControl = UIPageControl.appearance()
    pageControl.pageIndicatorTintColor = UIColor(netHex: 0xdddddd)
    pageControl.currentPageIndicatorTintColor = UIColor(netHex: 0x666666)
    pageControl.backgroundColor = UIColor.whiteColor()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment