Push анимация без теней и затемнений

У меня простая iOS NavigationController на основе приложения. Два UICollectionViews, один за другим. Если щелкнуть элемент в "первой коллекции", откроется "вторая коллекция". Достаточно просто.

Важная заметка:

"И то и другое UICollectionViews иметь прозрачный фон. Общий цвет фона для navigationController используется. (Унаследованный класс от UINavigationController ) "

Проблема: если все правильно поняли, нажмите метод NavigationController работает по алгоритму:

  1. Pushing view создан.
  2. Прозрачное серое наложение создается поверх толкающего вида.
  3. NavigationController перемещает представление со стандартной анимацией. (Серое наложение все еще там)
  4. Серое наложение исчезает.

(Если у толкающего вида прозрачный фон, видна серая вертикальная линия)

Скриншот

Следующий шаг: я попытался решить эту проблему, переопределив метод push. Вот что у меня есть:

- (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];
}

Этот способ создает свою собственную анимацию push, но были использованы другие стандартные анимации, которые я не могу удалить. (Затемнение при представлении и скрытии видов)

Screenshots_1_and_2

Вопрос: "Как я могу нажать ViewController, без затухания, затемнения и других фильтров анимации?"

Решения с названиями тем (на stackru.com)

  • IOS 7 UINavigationController Нажмите анимацию тени
  • iOS 7 показывает черный фон в пользовательской анимации для навигации

Не работает

3 ответа

Решение

Не переопределяйте метод push. iOS7 позволяет предоставлять контроллеры анимации для пользовательских переходов. Смотрите здесь для более подробной информации.

Я долго боролся с этим и в конце концов придумал довольно хакерский способ сделать это, который, кажется, отлично работает.

      class UINavigationControllerKillDim: UINavigationController {
            
        private weak var displayLink: CADisplayLink?
    
        override func pushViewController(_ viewController: UIViewController, animated: Bool) {
            displayLink?.invalidate()
            let displayLink = CADisplayLink(target: self, selector: #selector(linkTriggered))
            displayLink.add(to: .main, forMode: .default)
            self.displayLink = displayLink
            super.pushViewController(viewController, animated: animated)
        }
                
        override func viewDidDisappear(_ animated: Bool) {
            super.viewDidDisappear(animated)
            displayLink?.invalidate()
        }
        
        @objc func linkTriggered(displaylink: CADisplayLink) {                
            let parallaxViews = self.view.allParallaxSubviews()
            
            parallaxViews.forEach({ parallaxView in
                parallaxView.backgroundColor = UIColor.clear
            })
        }
        
        deinit{
            displayLink?.invalidate()
        }
        
    }
    
    private extension UIView {
            
        func allParallaxSubviews() -> [UIView]{
            var all = [UIView]()
            func getSubview(view: UIView) {
                if String(describing: type(of: view)) == "_UIParallaxDimmingView" {
                    all.append(view)
                }
                guard !view.subviews.isEmpty else { return }
                view.subviews.forEach{ getSubview(view: $0) }
            }
            getSubview(view: self)
            return all
        }
    }

В основном он находит каждый UIParallaxDimmingView (их 2) и устанавливает цвет фона для очистки в каждом кадре.

Я думаю, что использование setBackgroundColor в _UIParallaxDimmingView может быть лучшим путем, но я не мог понять, как использовать метод внутри частного класса.

Была такая же проблема с размытыми прозрачными контроллерами представления. Затемняющий вид откладывается во время перехода.

Я должен признать, что мне очень нравятся нативные переходы iOS (особенно с большими заголовками), и я думаю, что для их воспроизведения с тем же уровнем качества требуется довольно много времени. Я наткнулся на множество руководств, объясняющих, как создавать пользовательские переходы, но результат выглядел довольно странно (в Snapchat есть собственные интерактивные переходы UIScrollView со страницы на страницу, и лично я нахожу пользовательский опыт странным, потому что 90% других моих приложений имеют родные переходы Apple)

В любом случае, если у кого-то есть такая же проблема с нажатием полупрозрачных контроллеров представления, вот моя реализация .

Обратите внимание, что это все еще довольно глупо и может не работать в будущих версиях iOS. Поправьте меня, если я пропущу лучший способ «отменить» собственные переходы iOS VC.

      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
            })
        }
    }
    

}
Другие вопросы по тегам