Post

Replies

Boosts

Views

Activity

Reply to iOS 18 hit testing functionality differs from iOS 17
This code worked for me. The benefit here is that it uses the default behavior of the hitTest on iOS. It checks the alpha of the point, and also other conditions like whether the hit view is interactive and whether the rootViewController.view has subviews. The alpha point check was inspired by the swiftui-window-overlay by sunghyun-k. enum PassThroughWindowType { case ignoreAllTouches case allowInteractiveTouches } class PassThroughWindow: UIWindow { let type: PassThroughWindowType init(windowScene: UIWindowScene, type: PassThroughWindowType) { self.type = type super.init(windowScene: windowScene) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { guard type == .allowInteractiveTouches else { return nil } if #available(iOS 18, *) { // In iOS >= 18 versions, prevent rootViewController.view by check the alpha Of point guard let rootView = rootViewController?.view, let hitView = super.hitTest(point, with: event) else { return nil } // Avoid hit testing if there are no subviews or if view is non-interactive guard !rootView.subviews.isEmpty, hitView.isUserInteractionEnabled, !hitView.isHidden, hitView.alpha > 0 else { return nil } // if hitView == rootViewController.view && the alpha at the point < 0.01 .. then prevent rootViewController.view from capturing the hit if hitView == rootView, alphaOfPoint(in: rootView, at: point) < 0.01 { return nil } return hitView } else { // In older iOS versions, prevent rootViewController.view from capturing the hit guard let hitView = super.hitTest(point, with: event) else { return nil } return rootViewController?.view == hitView ? nil : hitView } } private func alphaOfPoint(in view: UIView, at point: CGPoint) -> CGFloat { var pixelData: [UInt8] = [0, 0, 0, 0] guard let context = CGContext( data: &pixelData, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue ) else { return .zero } context.translateBy(x: -point.x, y: -point.y) view.layer.render(in: context) return CGFloat(pixelData[3]) / 255.0 } }
Topic: UI Frameworks SubTopic: General Tags:
Oct ’24