I need to overlay an UIView
on top of SwiftUI
content. Instead of using UIViewController.present(_:animated:completion:)
, I simply add it to UIWindow
hierarchy and apply constraints (I need it for additional customization, not important here).
But I ran into a problem. Overlayed UIView
passes tap gestures through itself, and underlying SwiftUI
View
gets clicked. You can see, that successive clicks make screen redder and redder—they basically insert more subviews.
Weird thing is, that drag gestures, such as scrolling, don't go through.
Video here: http://i.stack.imgur.com/gRk8a.gif
What are the implications here? Why is tap going through, and not drag?
struct ContentView: View {
var body: some View {
ScrollView(content: {
Button("Click", action: overlayView)
})
}
private func overlayView() {
let overlay: UIView = .init()
overlay.translatesAutoresizingMaskIntoConstraints = false
overlay.isUserInteractionEnabled = true
overlay.backgroundColor = .red.withAlphaComponent(0.1)
let windowView: UIView = UIApplication.shared.activeWindow!.rootViewController!.view
windowView.addSubview(overlay)
NSLayoutConstraint.activate([
overlay.leadingAnchor.constraint(equalTo: windowView.leadingAnchor),
overlay.trailingAnchor.constraint(equalTo: windowView.trailingAnchor),
overlay.topAnchor.constraint(equalTo: windowView.topAnchor),
overlay.bottomAnchor.constraint(equalTo: windowView.bottomAnchor)
])
}
}
extension UIApplication {
var activeWindow: UIWindow? {
connectedScenes
.filter { $0.activationState == .foregroundActive }
.first { $0 is UIWindowScene }
.flatMap { $0 as? UIWindowScene }?
.windows
.first { $0.isKeyWindow }
}
}
The same problem doesn't occur when launching from pure UIKit
:
final class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
let button: UIButton = .init()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(overlayView), for: .touchUpInside)
button.setTitle("Click", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
@objc private func overlayView() {
let overlay: UIView = .init()
overlay.translatesAutoresizingMaskIntoConstraints = false
overlay.isUserInteractionEnabled = true
overlay.backgroundColor = .red.withAlphaComponent(0.1)
let windowView: UIView = UIApplication.shared.activeWindow!.rootViewController!.view
windowView.addSubview(overlay)
NSLayoutConstraint.activate([
overlay.leadingAnchor.constraint(equalTo: windowView.leadingAnchor),
overlay.trailingAnchor.constraint(equalTo: windowView.trailingAnchor),
overlay.topAnchor.constraint(equalTo: windowView.topAnchor),
overlay.bottomAnchor.constraint(equalTo: windowView.bottomAnchor)
])
}
}
I would appreciate if you could help with actually cutting off the tap gestures.