34

Recently I updated my xcode project to work with iOS 7, but i faced a big problem. Because my whole application has only one background image (UIImageView added to key window) and all views are transparent, I face a problem when pushing UIViewController, because pushed view controller overlaps previous view (you can see it in the picture here: http://grab.by/qp0k). I can predict that this is because in iOS 7 push transition has been changed, because now it slides half a screen. Maybe anyone knows how to fix this issue?

This is how I set my key windows

  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 
 UIImageView *background = [[UIImageView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
 background.image = [UIImage imageNamed:@"background.png"]; 
UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:self.viewController];
self.window.rootViewContro‌​ller = navi;
 [self.window makeKeyAndVisible];

Afterwards when user clicks on "start workout" button I push my next view as always:

workoutView *w = [[workoutView alloc]initWithNibName:@"workoutView" bundle:nil];
        [self.navigationController pushViewController:w animated:YES];
Edvardas
  • 586
  • 1
  • 4
  • 10

9 Answers9

19

I did this.

-(void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self.view setAlpha:0];
}

Do not forget re set alpha when come back.

- (void) viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.view setAlpha:1];
}
Alexandre
  • 410
  • 5
  • 21
  • This one makes some difference for navigate back to previous ViewController, but not working for navigate to a new ViewController. The old VC's `-viewWillDisappear:` and `-viewWillAppear:` are not called when navigate to a new one. – Zhao Xiang Sep 24 '13 at 03:02
  • 1
    And when navigate back to previous VC, the other VC can be seen for a second – Zhao Xiang Sep 24 '13 at 03:05
  • 1
    You first comment is strange but : "And when navigate back to previous VC, the other VC can be seen for a second" is unfortunately right... If anyone has a better solution, It should be helpful. – Alexandre Sep 24 '13 at 14:58
  • 5
    `- (void)viewWillAppear:(BOOL)animated { self.tableView.alpha = 1.0; }` `- (void)viewWillDisappear:(BOOL)animated { [UIView animateWithDuration:0.3 animations:^(){ self.tableView.alpha = 0.0; }]; }` – Timur Bernikovich Oct 24 '13 at 16:25
  • This is the simplest way to solve the issue. Apple not making good transitions on transparent background views is ridiculous! – Ethan Allen Feb 11 '14 at 21:39
15

I solved the problem by implementing the new UINavigationControllerDelegate Method animationControllerForOperation.

For example:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController     *)navigationController
                              animationControllerForOperation:(UINavigationControllerOperation)operation
                                           fromViewController:(UIViewController *)fromVC
                                             toViewController:(UIViewController *)toVC
{

PushTransition* transition = [PushTransition new];
[transition setNavigationControllerOperation: operation];

return transition;
}

PushTransition is a class that implements the UIViewControllerAnimatedTransitioning protocol and the two methods transitionDuration and animateTransition from that protocol. Additionally, i have added a property to pass the operation (tells me if it is a push or pop transition).

Just put the animation code for moving the views into the animateTransition as follows:

// the containerView is the superview during the animation process.
UIView *container = transitionContext.containerView;

UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

UIView *fromView = fromVC.view;
UIView *toView = toVC.view;
CGFloat containerWidth = container.frame.size.width;

// Set the needed frames to animate.

CGRect toInitialFrame = [container frame];
CGRect fromDestinationFrame = fromView.frame;

if ([self navigationControllerOperation] == UINavigationControllerOperationPush)
{
    toInitialFrame.origin.x = containerWidth;
    toView.frame = toInitialFrame;
    fromDestinationFrame.origin.x = -containerWidth;
}
else if ([self navigationControllerOperation] == UINavigationControllerOperationPop)
{
    toInitialFrame.origin.x = -containerWidth;
    toView.frame = toInitialFrame;
    fromDestinationFrame.origin.x = containerWidth;
}

// Create a screenshot of the toView.
UIView *move = [toView snapshotViewAfterScreenUpdates:YES];
move.frame = toView.frame;
[container addSubview:move];

[UIView animateWithDuration:TRANSITION_DURATION delay:0
     usingSpringWithDamping:1000 initialSpringVelocity:1
                    options:0 animations:^{
                        move.frame = container.frame;
                        fromView.frame = fromDestinationFrame;
                    }
                 completion:^(BOOL finished) {
                     if (![[container subviews] containsObject:toView])
                     {
                         [container addSubview:toView];
                     }

                     toView.frame = container.frame;
                     [fromView removeFromSuperview];
                     [move removeFromSuperview];
                     [transitionContext completeTransition: YES];
                 }];

described it and you can you are done. Additionally you can make any push or pop animation you like.

snoersnoer
  • 193
  • 7
  • Where is this "animation code for moving the views into the animateTransition method as JX2 described" you speak of? – race_carr Mar 19 '14 at 17:21
  • Please look at the update. I added some code for you. – snoersnoer Mar 20 '14 at 13:21
  • I don't see a need to create a snapshot view for the animation. Why not animate the real views directly? – Daniel Rinser Mar 31 '15 at 10:26
  • If i remember correctly, it was possible to interact with the UI elements of the UIViewController before the animation finished. – snoersnoer Apr 07 '15 at 11:48
  • @snoersnoer If that was the case you could just set `userInteractionEnabled` to false until the completion is called – keji Jun 16 '16 at 22:40
  • Here is the sift version: https://gist.github.com/k3zi/9ab2e57381349dcd7fd9abd6e029430b – keji Jun 16 '16 at 22:44
  • Hi, thanks for this answer though, if you wait before calling back the pop, the view is deleted and so the animation is between a transparent (non existing view) and the current view. Do you have any idea why ? Thanks a lot – Clad Clad Feb 24 '17 at 09:14
6

I fixed it by doing this when initialising the view:

self.view.clipsToBounds = YES;
Enrico Susatyo
  • 19,372
  • 18
  • 95
  • 156
4

You might want to look into a new iOS7 feature that allows you to define your own custom UIViewController transitions. Look in the docs for UIViewControllerTransitioningDelegate. Also, here's a link to an article about it: http://www.doubleencore.com/2013/09/ios-7-custom-transitions/

John Jacecko
  • 1,870
  • 1
  • 16
  • 12
1

Ah, now I understand the issue. You were right, seems to be caused by the previous UIViewController not being hidden after the transition (because of the new transition effect).

There doesn't seem to be any SDK method to control this behavior. Short of redesigning the app to not requiring the background be static, you'll probably have to roll your own navigation. OSNavigationController is a complete reimplementation of UINavigationController that might help you out. If they haven't updated to the iOS 7 transition, you'll probably be good to go. If they have you can always use an older version.

Kevin
  • 3,111
  • 1
  • 19
  • 26
1

I had the same problem. Try to load your background image in the init method. For me, it worked (sometimes): For example:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        self.view.backgroundColor = [UIColor whiteColor];
        [self.imageBack setImage:[UIImage imageNamed:@"mayBack.png"]];
    }
    return self;
}

However, you could see glimpses.. The best solution I found, beside implementing the new iOS7 transition protocol, is to implement a category, and use that category whenever you need it. You can find the answer here

Community
  • 1
  • 1
gdm
  • 7,647
  • 3
  • 41
  • 71
1

Setting the image to the background color solved the issue:

self.view.backgroundColor = 
            [UIColor colorWithPatternImage:[UIImage imageNamed:@"mainback.png"]];
OGHaza
  • 4,795
  • 7
  • 23
  • 29
0

Take a look at the UINavigationController category in that post (it solved me problem) :

https://stackoverflow.com/a/18882232/2826409

Community
  • 1
  • 1
Eran Katsav
  • 1,324
  • 15
  • 16
0

Props to @snoersnoer.


Here is the code in Swift 3.

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

    let pushTransition = SUPushTransition()
    pushTransition.navigationControllerOperation = operation
    return pushTransition
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

    // the containerView is the superview during the animation process.
    let container = transitionContext.containerView

    let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
    let toVC = transitionContext.viewController(forKey:UITransitionContextViewControllerKey.to);

    if let from = fromVC,
        let fromView = from.view,
        let to = toVC,
        let toView = to.view {

        let containerWidth = container.frame.size.width

        // Set the needed frames to animate.

        var toInitialFrame = container.frame
        var fromDestinationFrame = fromView.frame

        if self.navigationControllerOperation == .push {
            toInitialFrame.origin.x = containerWidth;
            toView.frame = toInitialFrame;
            fromDestinationFrame.origin.x = -containerWidth;
        }
        else if self.navigationControllerOperation == .pop {
            toInitialFrame.origin.x = -containerWidth;
            toView.frame = toInitialFrame;
            fromDestinationFrame.origin.x = containerWidth;
        }

        // Create a screenshot of the toView.
        if let move = toView.snapshotView(afterScreenUpdates: true) {

            move.frame = toView.frame
            container.addSubview(move)

            UIView.animate(withDuration: Constants.MainPage.navControllerDuration, delay: 0.0, usingSpringWithDamping: 1000, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
                move.frame = container.frame;
                fromView.frame = fromDestinationFrame;
            }, completion: { (finished) in

                if finished {

                    if !container.subviews.contains(toView) {
                        container.addSubview(toView)
                    }

                    toView.frame = container.frame

                    fromView.removeFromSuperview()
                    move.removeFromSuperview()

                    transitionContext.completeTransition(true)
                }

            })

        }

    }

}

Cheers.

Brandon A
  • 8,153
  • 3
  • 42
  • 77