5
votes

I have a simple iOS NavigationController based application. Two UICollectionViews, one after another. If element on "first collection" was clicked, the "second collection" will be opened. Quite simple.

Important note:

"Both UICollectionViews have transparent background. General background color for navigationController is used. (Inherited class from UINavigationController)"

The problem: If understood it right, push method of NavigationController works according to algorithm:

  1. Pushing view is created.
  2. Transparent gray overlay is created over pushing view.
  3. NavigationController pushes the view with standard animation. (Gray overlay still there)
  4. Gray overlay disappears.

(If pushing view has transparent background, a gray vertical line is visible)

Screenshot

Next step: I've tried to solve this problem by overriding push method. Here's what I've got:

- (void)pushViewController:(UIViewController *)viewController 
                           animated:(BOOL)animated
{
    CATransition *transition = [CATransition animation];
    transition.duration = 0.45;
    transition.timingFunction = [CAMediaTimingFunction 
           functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionPush;
    transition.subtype = kCATransitionFromRight;
    transition.fillMode = kCAFillModeForwards;
    transition.delegate = self;
    [self.view.layer addAnimation:transition forKey:nil];

    [super pushViewController:viewController animated:animated];
}

This way creates its own push animation, but there were another standard animations used, which I can't remove. (Blackout on presenting and hiding views)

Screenshots_1_and_2

Question: "How can I push ViewController, without fading, blackout, and other animation filters?"

Solutions with theme names (on stackoverflow.com)

  • iOS 7 UINavigationController Push animation shadow
  • iOS 7 shows black background in custom animation for Navigation

Don't work.

2

2 Answers

5
votes

Don't override the push method. iOS7 allows you to provide animation controllers for custom transitions. See here for more details.

0
votes

Had the same problem with blurred transparent view controllers. A dimming view is put behind during the transition.

I have to admit I really like iOS native transitions (especially with larges titles), and I guess it is pretty time consuming to reproduce them with the same level of quality. I came across numerous tutorials explaining how to create custom transitions but the result looked quite weird (Snapchat has its own UIScrollView interactive transitions from pages to pages, and personally, I find user experience weird because 90% of my other apps have native Apple transitions)

Anyway, if someone has the same problem of pushing translucent view controllers, here is my implementation of pushViewController.

Please note this is still quite silly and may not work for future versions of iOS. Correct me if I miss a better way to "override" native iOS VC transitions.

override func pushViewController(_ viewController: UIViewController, animated: Bool)
{
    let visibleVc = self.visibleViewController
    
    super.pushViewController(viewController, animated: animated)
    if(animated)
    {
        UIView.animate(withDuration:transitionCoordinator?.transitionDuration ?? 0.2, animations: {
            visibleVc?.view.alpha = 0
        })
    }
    
    // at least in iOS 13, pushing a viewController creates, the time of the transition,
    // a dimming view that disappear only after the old view controller is removed.
    // this causes the blur to jump a tiny bit in brightness at the end of the push animation
    // to remove that "jump", we find the dimming view after a while (it's not present at the beginning), and animate it to alpha 0, from half animation time
    // FIXME: this is only a silly trick : it would be appropriate to create our own VC transition later 
    // Works at least with iOS 13, to be tested with iOS < 13 and iOS 14

    let halfDuration = (transitionCoordinator?.transitionDuration ?? 0.2) / 2
    DispatchQueue.main.asyncAfter(deadline:.now() + halfDuration ) {
        if let uiNavigationTransitionView = self.view.subviews.first(where: {
            String(describing: type(of: $0)) == "UINavigationTransitionView"
        }),
        let wrapperView = uiNavigationTransitionView.subviews.first(where: {
            String(describing: type(of: $0)) == "UIViewControllerWrapperView"
        }),
        let grayOverlay = wrapperView.subviews.first(where: {
            String(describing: type(of: $0)) == "_UIParallaxDimmingView"
        })
        {
            UIView.animate(withDuration:halfDuration, animations: {
                grayOverlay.alpha = 0
            })
        }
    }
    

}