Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laosb/c32df685ae46cca690868910dacf6f78 to your computer and use it in GitHub Desktop.
Save laosb/c32df685ae46cca690868910dacf6f78 to your computer and use it in GitHub Desktop.
Fix SwiftUI Menu Backdrop Penetration.
import UIKit
// https://devnotes.by.sb/apple/swiftui/menu-backdrop-not-blocking-user-interactions
class SwiftUIMenuBackdropPenetrationFix: UIView {
// _UIContextMenuPlatterTransitionView
static let transitionViewClass: AnyClass? = NSClassFromString("_UIContextMenuPlatterTransitionView")
// _UIContextMenuContainerView
static let containerViewClass: AnyClass? = NSClassFromString("_UIContextMenuContainerView")
static func swizzle() {
let didAddSubviewSelector = #selector(didAddSubview(_:))
let willRemoveSubviewSelector = #selector(willRemoveSubview(_:))
let willMoveToWindowSelector = #selector(willMove(toWindow:))
let originalDidAddSubviewMethod = class_getInstanceMethod(
SwiftUIMenuBackdropPenetrationFix.containerViewClass,
didAddSubviewSelector
)
let originalWillRemoveSubviewMethod = class_getInstanceMethod(
SwiftUIMenuBackdropPenetrationFix.containerViewClass,
willRemoveSubviewSelector
)
let originalWillMoveToWindowMethod = class_getInstanceMethod(
SwiftUIMenuBackdropPenetrationFix.containerViewClass,
willMoveToWindowSelector
)
let swizzledDidAddSubviewMethod = class_getInstanceMethod(
SwiftUIMenuBackdropPenetrationFix.self,
didAddSubviewSelector
)
let swizzledWillRemoveSubviewMethod = class_getInstanceMethod(
SwiftUIMenuBackdropPenetrationFix.self,
willRemoveSubviewSelector
)
let swizzledWillMoveToWindowMethod = class_getInstanceMethod(
SwiftUIMenuBackdropPenetrationFix.self,
willMoveToWindowSelector
)
guard
let swizzledDidAddSubviewMethod,
let swizzledWillRemoveSubviewMethod,
let swizzledWillMoveToWindowMethod
else {
fatalError("SwiftUIMenuBackdropPenetrationFix: Swizzled methods don't exist.")
}
if let originalDidAddSubviewMethod {
method_exchangeImplementations(originalDidAddSubviewMethod, swizzledDidAddSubviewMethod)
} else {
let imp = method_getImplementation(swizzledDidAddSubviewMethod)
let types = method_getTypeEncoding(swizzledDidAddSubviewMethod)
class_addMethod(
SwiftUIMenuBackdropPenetrationFix.containerViewClass,
didAddSubviewSelector,
imp,
types
)
}
if let originalWillRemoveSubviewMethod {
method_exchangeImplementations(originalWillRemoveSubviewMethod, swizzledWillRemoveSubviewMethod)
} else {
let imp = method_getImplementation(swizzledWillRemoveSubviewMethod)
let types = method_getTypeEncoding(swizzledWillRemoveSubviewMethod)
class_addMethod(
SwiftUIMenuBackdropPenetrationFix.containerViewClass,
willRemoveSubviewSelector,
imp,
types
)
}
if let originalWillMoveToWindowMethod {
method_exchangeImplementations(originalWillMoveToWindowMethod, swizzledWillMoveToWindowMethod)
} else {
let imp = method_getImplementation(swizzledWillMoveToWindowMethod)
let types = method_getTypeEncoding(swizzledWillMoveToWindowMethod)
class_addMethod(
SwiftUIMenuBackdropPenetrationFix.containerViewClass,
willMoveToWindowSelector,
imp,
types
)
}
}
@objc dynamic override func didAddSubview(_ subview: UIView) {
// self.didAddSubview(subview)
guard type(of: self) == SwiftUIMenuBackdropPenetrationFix.containerViewClass else { return }
if type(of: subview) == SwiftUIMenuBackdropPenetrationFix.transitionViewClass {
print("SwiftUIMenuBackdropPenetrationFix: \(self) Did add transitionViewClass")
self.window?.rootViewController?.view.isUserInteractionEnabled = false
}
}
@objc dynamic override func willRemoveSubview(_ subview: UIView) {
// self.willRemoveSubview(subview)
guard type(of: self) == SwiftUIMenuBackdropPenetrationFix.containerViewClass else { return }
if type(of: subview) == SwiftUIMenuBackdropPenetrationFix.transitionViewClass {
print("SwiftUIMenuBackdropPenetrationFix: \(self) Remove transitionViewClass")
self.window?.rootViewController?.view.isUserInteractionEnabled = true
}
}
@objc dynamic override func willMove(toWindow newWindow: UIWindow?) {
guard type(of: self) == SwiftUIMenuBackdropPenetrationFix.containerViewClass else { return }
if newWindow == nil {
print("SwiftUIMenuBackdropPenetrationFix: \(self) Will be removed")
self.window?.rootViewController?.view.isUserInteractionEnabled = true
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment