Search code examples
iosswiftgestureuiviewcontrollerrepresentable

Defer system edge gestures in only some view controllers. SwiftUI


I want to navigate to a custom UIView where the system edge gestures are disabled. I am using the SwiftUI life cycle with UIViewControllerRepresentable and overriding preferredScreenEdgesDeferringSystemGestures.

I have seen the solutions with SceneDelegates. Does preferredScreenEdgesDeferringSystemGestures have to act on window.rootViewController for it to work?

class MyUIViewController: UIViewController {
    typealias UIViewControllerType = MyUIViewController
    open override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
        return [.all];
    }
    let labelDescription: UILabel = {
        let label = UILabel()
        label.text = "But it's not working."
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(labelDescription)
        labelDescription.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
        setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
    }
}
struct UIViewControllerRepresentation : UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> some UIViewController {
        let uiViewController = MyUIViewController()
        return uiViewController
    }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}
struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink("To UIView with no system edge gestures.",
                           destination: UIViewControllerRepresentation())
                .navigationTitle("Title")
        }
    }
}

Solution

  • https://developer.apple.com/documentation/uikit/uiviewcontroller/2887511-childforscreenedgesdeferringsyst

    If I understand it correctly the system only asks the first UIViewController and if that vc doesn't return a child that the system should ask too then that's it.

    Since you don't have access to the view controllers in SwiftUI (or even know what types of view controllers it will use) I opted to just swizzle the childForScreenEdgesDeferringSystemGestures and childForHomeIndicatorAutoHidden getters and return the view controller that manages these for me by looping over all the UIViewController.children.

    Since you linked to this question from my Gist I will link back there for the solution which is specific to my Gist. https://gist.github.com/Amzd/01e1f69ecbc4c82c8586dcd292b1d30d