背景模糊的模态导航控制器,在推动画期间是否无缝?

时间:2018-10-12 02:28:17

标签: ios uinavigationcontroller uivisualeffectview

我将以表格视图控制器为根,以模态形式展示导航控制器,并在用户点击一个单元格时推送更多视图控制器。现在,我想对整个对象应用模糊/鲜艳效果。

故事板如下:

enter image description here

现在,要将模糊效果应用于整个导航控制器,以便可以在下面看到初始视图控制器中的图像。

通过对表视图控制器和详细视图控制器应用相同的模糊效果,我已经取得了一些成功,

表视图控制器

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.tableFooterView = UIView(frame: .zero)

        tableView.backgroundColor = UIColor.clear
        let blurEffect = UIBlurEffect(style: .dark)
        let blurEffectView = UIVisualEffectView(effect: blurEffect)
        tableView.backgroundView = blurEffectView
        tableView.separatorEffect = UIVibrancyEffect(blurEffect: blurEffect)
    }
}

详细视图控制器

class DetailViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .clear
        let blurEffect = UIBlurEffect(style: .dark)
        let blurView = UIVisualEffectView(effect: blurEffect)
        blurView.translatesAutoresizingMaskIntoConstraints = false
        view.insertSubview(blurView, at: 0)
        NSLayoutConstraint.activate([
            blurView.heightAnchor.constraint(equalTo: view.heightAnchor),
            blurView.widthAnchor.constraint(equalTo: view.widthAnchor),
        ])
    }
}

(我还将导航的模式显示样式设置为.overFullScreen

但是,在导航到详细信息视图控制器的最后,在模糊的背景中可以看到烦人的人工产物

enter image description here

我认为,这与自iOS 7开始,推送视图控制器不能具有透明背景有关,否则可以看到推送视图控制器,因为它在过渡结束时立即被移除了。

我尝试将模糊效果应用于导航控制器,并改为使两个孩子的视图都透明,但效果也不佳。

将背景模糊/鲜艳效果无缝应用于导航控制器内容的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

尝试以下步骤。我没有尝试过,但应该可以。

  1. 将背景图像添加到didFinishLaunchingWithOptions的窗口中,并默认将其隐藏。
  2. 将相同的图像添加到演示模式视图控制器中。
  3. 确保清除视图控制器的背景。
  4. 因此,当您展示它时,它将显示该图像到模态视图控制器中。
  5. 完成后,取消隐藏窗口图像并隐藏视图控制器图像。
  6. 点击后退按钮以关闭视图时,首先取消隐藏视图控制器图像并隐藏窗口图像。这样您的动画看起来会很正常。

让我知道您是否需要任何帮助。

答案 1 :(得分:0)

正如我在问题中指出的那样,自iOS 7以来引入的推送/弹出动画过渡(其中导航堆栈中较低的视图控制器“以一半的速度滑出”,因此(它比被推入/弹出的窗口要小)。无法在没有构件的透明视图控制器之间进行转换。

因此,我决定采用协议UINavigationControllerDelegate,更确切地说是方法:navigationController(_:animationControllerFor:from:to:)从头开始基本上复制UINavigationController的动画过渡。

这使我可以在推操作期间从过渡移开的视图中添加mask view,以便裁剪下重叠部分,并且不会出现可见的伪像。

到目前为止,我仅实现了 push 操作(最终,我也必须执行pop操作,还需要基于手势的弹出式交互转换)。 / strike> 我已经创建了一个自定义的UINavigationController子类put it on GitHub

(目前,它支持动画推送和弹出,但不支持交互性。而且,我还没有弄清楚如何复制导航栏标题的“幻灯片”动画-它只是交叉溶解。)

它可以归纳为以下步骤:

  1. 最初,将.to视图转换到屏幕外的右侧。在动画过程中,逐渐将其转换回身份(屏幕中心)。
  2. 将{strong>白色UIView 设置为mask视图,.from的边界最初等于.from视图(无遮罩)。在动画过程中,逐渐将蒙版的frame属性的宽度减小到其初始值的一半。
  3. 在动画过程中,将from视图逐渐移出屏幕一半,向左移。

在代码中:

class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {

    // Super slow for debugging:
    let duration: TimeInterval = 1

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromView = transitionContext.view(forKey: .from) else {
            return
        }
        guard let toView = transitionContext.view(forKey: .to) else {
            return
        }
        guard let toViewController = transitionContext.viewController(forKey: .to) else {
            return
        }

        //
        // (Code below assumes navigation PUSH operation. For POP, 
        //  use similar code but with view roles and direction 
        //  reversed)
        //

        // Add target view to container:
        transitionContext.containerView.addSubview(toView)

        // Set tagret view frame, centered on screen
        let toViewFinalFrame = transitionContext.finalFrame(for: toViewController)
        toView.frame = toViewFinalFrame
        let containerBounds = transitionContext.containerView.bounds
        toView.center = CGPoint(x: containerBounds.midX, y: containerBounds.midY)

        // But translate it to fully the RIGHT, for now:
        toView.transform = CGAffineTransform(translationX: containerBounds.width, y: 0)

        // We will slide the source view out, to the LEFT, by half as much:
        let fromTransform = CGAffineTransform(translationX: -0.5*containerBounds.width, y: 0)

        // Apply a white UIView as mask to the source view:
        let maskView = UIView(frame: CGRect(origin: .zero, size: fromView.frame.size))
        maskView.backgroundColor = .white
        fromView.mask = maskView

        // The mask will shrink to half the width during the animation:
        let maskViewNewFrame = CGRect(origin: .zero, size: CGSize(width: 0.5*fromView.frame.width, height: fromView.frame.height))

        // Animate:
        UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseOut], animations: {

            fromView.transform = fromTransform // off-screen to the left (half)

            toView.transform = .identity // Back on screen, fully centered

            maskView.frame = maskViewNewFrame // Mask to half width

        }, completion: {(completed) in

            // Remove mask, or funny things will happen to a 
            // table view or scroll view if the user 
            // "rubber-bands":
            fromView.mask = nil

            transitionContext.completeTransition(completed)
        })
    }

结果:

enter image description here

(为清楚起见,我向详细视图控制器添加了文本视图)