34
votes

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];
9
Can you add the code you are using to set the key window and the first view controller?Kevin
@Kevin Updated my question!Edvardas
@Edvardas did you solve this while maintaining your single image background? I have the same problem.Tom Redman
@Edvardas, I'm also looking for a solution hereZhao Xiang

9 Answers

15
votes

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.

19
votes

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];
}
6
votes

I fixed it by doing this when initialising the view:

self.view.clipsToBounds = YES;
4
votes

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/

1
votes

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.

1
votes

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

1
votes

Setting the image to the background color solved the issue:

self.view.backgroundColor = 
            [UIColor colorWithPatternImage:[UIImage imageNamed:@"mainback.png"]];
0
votes

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

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

0
votes

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.