在被呈现后,视图被UITransitionView阻挡。

26
我有一个侧边导航控制器,并通过UIButton呈现它。当我通过[self presentviewcontroller: NC animated: YES completion: nil]将此NC直接设置为根视图控制器时,由于某种原因,NC的菜单侧面被一个UITransitionView阻挡,我无法让它消失。

Enter image description here

Enter image description here

我已经尝试了以下方法:

UIWindow *window = [(AppDelegate *)[[UIApplication sharedApplication] delegate] window];
    window.backgroundColor = kmain;


    CATransition* transition = [CATransition animation];
    transition.duration = .5;
    transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    transition.type = kCATransitionPush;
    transition.subtype = kCATransitionFromTop;

    [nc.view.layer addAnimation:transition forKey:kCATransition];

    [UIView transitionWithView:window
                      duration:0.5
                       options:UIViewAnimationOptionTransitionNone
                    animations:^{ window.rootViewController = nc; }
                    completion:^(BOOL finished) {
                        for (UIView *subview in window.subviews) {
                            if ([subview isKindOfClass:NSClassFromString(@"UITransitionView")]) {
                                [subview removeFromSuperview];
                            }
                        }
                    }];

但这种方法非常不专业,由于窗口的根视图控制器在转换过程中会发生变化,因此会有一些卡顿,导航控制器和右上角的一部分会变黑,看起来非常糟糕。


你找到根本原因了吗?我在这里遇到了同样的问题。移除视图或禁用视图上的触摸似乎是一种解决方法,但我们如何避免它呢?我不明白为什么会出现这个问题。 - RainCast
那是很久以前的事了,我不太确定,但我相信我完全切换了库并选择了JASidePannelController:https://github.com/gotosleep/JASidePanels使用起来更加容易。 - Jameson
这个问题终于解决了:https://dev59.com/u1sW5IYBdhLWcg3wuJKd#53922625 - Fattie
6个回答

8
要通过UITransitionView获取触摸事件,将containerViewuserInteractionEnabled设置为false。如果您正在使用UIViewControllerAnimatedTransitioning进行自定义过渡动画,则需要这样做。
例如,在animateTransition(_:)函数中:
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

    let containerView = transitionContext.containerView
    containerView.isUserInteractionEnabled = false

    ...
}

10
如果我将 containerView 的 interactions 属性设置为 disabled,那么对呈现视图的点击也会穿透过去。 - Alper
有解决方案吗? - yasirmturk
@Alper 这是解决方法:https://dev59.com/AVoV5IYBdhLWcg3wivL9#63497131 - Lance Samaria
1
这是错误的。问题在于您无法访问UITransitionView。以下是解决方案:https://dev59.com/u1sW5IYBdhLWcg3wuJKd#53922625(此答案是您必须经常使用的重要技术,以“穿过”VC中的视图,但要向后退,您需要使用此 https://dev59.com/u1sW5IYBdhLWcg3wuJKd#53922625) - Fattie

5
在我的情况下,我需要一个半屏幕大小的视图控制器。我遵循了这个答案,它非常好用,直到我意识到我仍然需要能够与呈现VC(半尺寸VC后面的VC)交互。
关键是你必须使用相同的CGRect值设置这两个框架: halfSizeVC.frame = CGRect(x: 0, y: UIScreen.main.bounds.height / 2, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) containerView = CGRect(x: 0, y: UIScreen.main.bounds.height / 2, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) 以下代码可将ViewController转换为HalfSizeController并使HalfSizeController为屏幕大小的1/2。即使有halfSizeVC在屏幕上,您仍然可以与其后面的vc的顶部交互。
如果要触摸半尺寸VC中的某些内容,您还必须创建PassthroughView类。我在底部包含了它。
呈现VC是白色的,并带有底部的紫色按钮。点击紫色按钮将弹出红色的半尺寸VC。
vc/presentingVC:
import UIKit

class ViewController: UIViewController {

    lazy var purpleButton: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Tap to Present HalfSizeVC", for: .normal)
        button.setTitleColor(UIColor.white, for: .normal)
        button.backgroundColor = UIColor.systemPurple
        button.addTarget(self, action: #selector(purpleButtonPressed), for: .touchUpInside)
        button.layer.cornerRadius = 7
        button.layer.masksToBounds = true
        return button
    }()

    var halfSizeVC: HalfSizeController?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        // tap gesture on vc will dismiss HalfSizeVC
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissHalfSizeVC))
        view.addGestureRecognizer(tapGesture)
    }


    // tapping the purple button presents HalfSizeVC
    @objc func purpleButtonPressed() {

        halfSizeVC = HalfSizeController()

        // *** IMPORTANT ***
        halfSizeVC!.view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 2)

        halfSizeVC!.modalPresentationStyle = .custom

        present(halfSizeVC!, animated: true, completion: nil)
    }

    // dismiss HalfSizeVC by tapping anywhere on the white background
    @objc func dismissHalfSizeVC() {

        halfSizeVC?.dismissVC()
    }
}

halfSizeVC/presentedVC

import UIKit

class HalfSizeController: UIViewController {

    init() {
        super.init(nibName: nil, bundle: nil)
        modalPresentationStyle = .custom
        transitioningDelegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    lazy var topHalfDummyView: PassthroughView = {
        let view = PassthroughView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .clear
        view.isUserInteractionEnabled = true
        return view
    }()

    var isPresenting = false
    let halfScreenHeight = UIScreen.main.bounds.height / 2

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red

        setAnchors()
    }

    private func setAnchors() {
    
        view.addSubview(topHalfDummyView)
        topHalfDummyView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        topHalfDummyView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        topHalfDummyView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        topHalfDummyView.heightAnchor.constraint(equalToConstant: halfScreenHeight).isActive = true
    }

    public func dismissVC() {
        dismiss(animated: true, completion: nil)
    }
}

extension HalfSizeController: UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return self
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return self
    }

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

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        let containerView = transitionContext.containerView

        // *** IMPORTANT ***
        containerView.frame = CGRect(x: 0, y: halfScreenHeight, width: UIScreen.main.bounds.width, height: halfScreenHeight)

        let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
        guard let toVC = toViewController else { return }
        isPresenting = !isPresenting

        if isPresenting == true {
            containerView.addSubview(toVC.view)

            topHalfDummyView.frame.origin.y += halfScreenHeight

            UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {

                self.topHalfDummyView.frame.origin.y -= self.halfScreenHeight

            }, completion: { (finished) in
                transitionContext.completeTransition(true)
            })

        } else {
            UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {
            
            }, completion: { (finished) in
                self.topHalfDummyView.frame.origin.y += self.halfScreenHeight
                transitionContext.completeTransition(true)
            })
        }
    }
}

需要在HalfSizeVC中的topHalfDummyView中使用PassthroughView。
import UIKit

class PassthroughView: UIView {
    
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        print("Passing all touches to the next view (if any), in the view stack.")
        return false
    }
}

按下紫色按钮之前:

输入图像描述

按下紫色按钮后:

输入图像描述

如果您点击白色背景,则红色颜色将被取消

您只需复制粘贴这3个文件并运行您的项目


这应该是一个被接受的答案。 - Leonid Silver
1
这并不是真正的解决方案,只在极少数情况下有效。幸运的是,问题已经得到解决:https://dev59.com/u1sW5IYBdhLWcg3wuJKd#53922625 - Fattie
1
@Fattie 1- 它确实有效,因为我在自己的实时应用程序中使用它。2- 你测试过它吗?如果它没有工作,那么是哪个部分出了问题?3- 你说“这不是真正的解决方案”,但又说“只在少数情况下有效”。如果它在少数情况下有效,那么它就是一个解决方案。 - Lance Samaria
在我的情况下,我需要一个半尺寸的视图控制器。它只适用于那种情况。 - Fattie
1
@Fattie 我看了一下,但还没有尝试过。我这周晚些时候会试一下。祝好! - Lance Samaria
显示剩余3条评论

3

我遇到了类似的问题,一个UITransitionView一直阻止我的视图,导致用户无法进行任何交互。

在我的情况下,这是由于未完成自定义动画的UIViewController转换所致。

我忘记了使用以下代码正确地完成转换:

TransitionContext.completeTransition(transitionContext.transitionWasCancelled)

或者
TransitionContext.completeTransition(!transitionContext.transitionWasCancelled)

In the

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {}

来自于 UIViewControllerAnimatedTransitioning 协议。


我遇到了同样的问题。我有一个显示动画的进度条。当它完成时,我会关闭进度条并使用navigationController?.popToRootViewController返回到根视图控制器。也许在视图控制器弹出之前进度条没有被关闭。我通过确保在关闭进度条后再弹出视图控制器来解决这个问题。 - timyau

1

我遇到了同样的问题,但情况略有不同。最终我做了类似的事情来查找视图,但是与其删除可能更麻烦的视图,我禁用了用户交互,这样任何触摸事件都会穿过它,其他对象可以处理用户的交互。

在我的情况下,这只出现在将应用程序更新到iOS 10之后,而在iOS 9中运行的相同代码没有出现这种情况。


0

我在设置弹出视图控制器的accessibilityElements时遇到了这个问题。我通过删除元素数组的分配来解决它。


0

我曾经遇到同样的问题,这个方法解决了我的问题:

navigationController.setNavigationBarHidden(true, animated: false)

因为我在视图控制器中使用了自定义导航栏视图,所以这个方法对我很有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接