首页 > 解决方案 > 当 z-index 设置为非零值时,未在视图上调用触摸代理

问题描述

我正在实现聊天气泡功能。我希望聊天气泡位于所有视图控制器之上,并且不受屏幕转换的影响。所以我决定将它作为子视图添加到UIWindow到目前为止它看起来像

在此处输入图像描述

这很好用,但是如果我呈现视图控制器而不是推送,那么聊天头会落后于呈现的视图控制器。为了避免它,我设置了聊天头层的 ZIndex 属性

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if let window = UIApplication.shared.keyWindow {
            window.addSubview(chatHead)
            chatHead.layer.zPosition = 1
        }
    }

但是现在虽然聊天头出现在呈现的视图控制器的顶部,它的触摸代表像touchesBegan, ,当聊天头在呈现的 ViewController 之上时touchesMoved,什么都不会被调用touchesEnded

添加聊天头的代码(这个问题可能不需要很多代码,但如果有人想尝试就发布它)

class ChatHeadView: UIView {
    let headerWidth: CGFloat = 80.0
    let headerHeight: CGFloat = 80.0
    let sideInset: CGFloat = 10.0
    let bottomInset: CGFloat = 10.0
    var cancellationWindowShown = false
    
    lazy var cancellationContainerRect: CGRect = {
        var rect = self.cancelChatView.convert(self.cancelChatView.frame, to: nil)
        rect.origin.x = rect.origin.x + 30
        rect.size.height = 100
        return rect
    }()
    
    lazy var cancellationThreshold: CGFloat = {
        return self.keywindow.frame.maxY - (self.keywindow.frame.maxY / 3)
    }()
    
    lazy var keywindow: UIWindow = {
        let window = UIApplication.shared.keyWindow ?? UIWindow()
        return window
    }()
    
    lazy var cancelChatView: CancelChatView = {
        if let cancelView = Bundle.main.loadNibNamed("CancelChatView", owner: nil, options: [:])?.first as? CancelChatView {
            return cancelView
        }
        fatalError("Couldnt load")
    }()
    
    init() {
        super.init(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
        self.layer.masksToBounds = true
        self.layer.cornerRadius = 40
        self.layer.backgroundColor = UIColor.red.cgColor
        
        self.cancelChatView.frame = CGRect(x: 0, y: self.keywindow.frame.maxY - 140, width: self.keywindow.frame.width, height: 140)
        self.cancelChatView.layer.opacity = 0.0
        self.keywindow.addSubview(self.cancelChatView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        debugPrint("Touch started")
    }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let location = touch.location(in: self.keywindow)
            if location.y >= cancellationThreshold {
                self.showCancellationWindow()
            }
            else {
                removeCancellationView()
                debugPrint("Removing Cancellation window")
            }
            self.center = location
        }
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first {
            let location = touch.location(in: self.keywindow)
            self.center = location
            self.snap(to: location)
            self.removeCancellationView()
        }
    }
    
    private func snap(to point: CGPoint) {
        var finalPoint:CGPoint = self.frame.origin
        
        if self.cancellationContainerRect.intersects(self.convert(self.frame, to: nil)) {
            self.removeFromSuperview()
        }
        else {
            if finalPoint.x <= self.keywindow.center.x {
                finalPoint.x = self.keywindow.frame.minX + sideInset
            }
            else if finalPoint.x > self.keywindow.center.x {
                finalPoint.x = self.keywindow.frame.maxX - (sideInset + self.headerWidth)
            }
            
            UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.6, options: .curveEaseInOut, animations: {
                self.frame.origin = finalPoint
            }, completion: nil)
        }
    }
    
    private func showCancellationWindow() {
        if cancellationWindowShown == false {
            let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
            animation.fromValue = 0.0
            animation.toValue = 0.6
            animation.duration = 0.1
            animation.fillMode = .forwards
            animation.isRemovedOnCompletion = false
            animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
            self.cancelChatView.layer.add(animation, forKey: "reveal")
            self.cancellationWindowShown = true
        }
    }
    
    private func removeCancellationView() {
        if cancellationWindowShown == true {
            let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
            animation.toValue = 0.0
            animation.duration = 0.1
            animation.fillMode = .forwards
            animation.isRemovedOnCompletion = false
            animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
            self.cancelChatView.layer.add(animation, forKey: "hide")
            cancellationWindowShown = false
        }
    }
}

问题 Gif(触摸不起作用):

在此处输入图像描述

正如马特询问我如何呈现 ViewController 的详细信息,添加故事板 segue 配置的屏幕截图

在此处输入图像描述

标签: iosswift

解决方案


推荐阅读