preferredScreenEdgesDeferringSystemGestures in keyboard extension doesnt work
Asked Answered
E

2

2

I trying to make keyboard extension on SwiftUI. My keyboard uses gestures a lot. But gestures up from keys located near the screen bottom are to be recognized as a system gesture. As it was said here I tried to use preferredScreenEdgesDeferringSystemGestures parameter of UIInputViewController but it does not help.

here is my code:

import UIKit import SwiftUI //import CoreData

struct SwiftUIContainer: View{
    @State var dragLocation: CGPoint = .zero
    var body: some View{
        VStack{
            Circle()
                .fill(Color.red)
                .frame(width: 20, height: 20)
                .position(self.dragLocation)
                .gesture(DragGesture().onChanged(){value in
                    self.dragLocation = value.location
                }
            )
        }.background(Color.green)
    }
}

class MyController<Content: View>: UIHostingController<Content>{
    open override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge{
        print("request edges for conteiner")
        return [.bottom]
    }
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("set edges for conteiner")
        setNeedsUpdateOfScreenEdgesDeferringSystemGestures() // better to call it here
    }
}

class KeyboardViewController: UIInputViewController {
    @IBOutlet var nextKeyboardButton: UIButton!
    var swiftUIConteiner: UIViewController!
    override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge{
        print("request edges for keyboard")
        return [.bottom]
    }
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        let swiftUIView = SwiftUIContainer()
        let child = MyController(rootView: swiftUIView)
        child.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.swiftUIConteiner = child as UIViewController
        //  child.setNeedsUpdateOfScreenEdgesDeferringSystemGestures()
        print("create children for keyboard")

    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override func viewDidLoad(){
        super.viewDidLoad()
        view.addSubview(self.swiftUIConteiner.view)
        addChild(self.swiftUIConteiner)
        print("insert children for keyboard")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("set edges for keyboard")
        setNeedsUpdateOfScreenEdgesDeferringSystemGestures() // better to call it here
    }

    override var childForScreenEdgesDeferringSystemGestures: UIViewController? {
        print("show children for keyboard")
        return self.swiftUIConteiner
    }
}

I tried both, override UIHostingController as a subclass, and override UIInputViewController. nothin helps. Is it possible for keyboard to disable system bottom screen edge gesture?

Here how it works now:

here is how it works in keyboard extension

and here is how this works in fullscreen app and I want the same with my keyboard:

and here is how this works in fullscreen app and I want the same with my keyboard

Enhanced answered 15/4, 2020 at 11:51 Comment(4)
Do you test on a device? And which dev environment do you use? I observe a bit different behaviour in my test. Would you give access to your entire project, GitHub, etc.?Archfiend
@Archfiend here's the gitHub link. It has the same behavior like shown in question. I tested it in iPhone 8 and SE simulator and SE real device. Newer models have some space between screen bottom and keyboard bottom making this problem not very important. But those 2 models... By the way, it fails to launch first couple of times (didn't get why yet) but after that if worksEnhanced
Xcode Version 11.4 (11E146) macOS Catalina 10.15.4 (19E287)Enhanced
Look at https://mcmap.net/q/1999772/-preferredscreenedgesdeferringsystemgestures-in-swiftui with native SwiftUI modifier.Archfiend
A
1

Try updated. See comments inline.

class KeyboardViewController: UIInputViewController {
    @IBOutlet var nextKeyboardButton: UIButton!
    let device = Device()
    override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge{
        return [.bottom]
    }
    override func updateViewConstraints() {
        super.updateViewConstraints()
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        let setting = KeyboardSettings.Default
        let swiftUIView = SwiftUIContainer(inputObjectDelegate: self, setting: setting)

        let child = MyController(rootView: swiftUIView.environmentObject(device))
        child.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(child.view)
        addChild(child)

        let heightConstraint = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: CGFloat(setting.height))
        view.addConstraint(heightConstraint)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        setNeedsUpdateOfScreenEdgesDeferringSystemGestures() // better to call it here
    }

    // required if child controller used
    override var childForScreenEdgesDeferringSystemGestures: UIViewController? {
        return self.children.first // should be MyController, or provide explicitly
    }
}
Archfiend answered 7/5, 2020 at 6:48 Comment(8)
thanx for advise. Updated code in my post removing all unnecessary things that could prevent you from launch it. And add implementation of your advise. But its steel shows system window at the first gesture unlike of fullscreen app. or I did something wrong?Enhanced
@Aspid, do you have any other view controllers in hierarchy (above or below)?Archfiend
no, I made it maximum simple. 'swiftUIView' is the only View I made. Look updated code - SwiftUIView is just Text() and its not workingEnhanced
update question: added gifs to show what's wrong and update a code to current version I triedEnhanced
Im desperate. Is it impossible to implement that logic in keyboard extension? That behavior ruins my keyboard interaction. I would really glad to get some help if it possible.Enhanced
I gave previous bounty (+50) to you because you at least tried to help. Started another bounty (+150) to someone who could help me to get what I want.Enhanced
@Aspid, well... I think this is most what can be achieve for now, after much of experimenting. It should work, and it does work if use the same controller (as KeyboardViewController: UIViewController) in normal app, but does not as keyboard extension. I finalise in thought that it is keyboard extension subsystem/engine limitation, because all callbacks are called correctly. Probably worth submitting feedback to Apple.Archfiend
Im upset. Wrote to support. Anyway, thank you a lot. If no one suggest something, bounty goes to you again.Enhanced
R
0

Suffered from the same issue and finally resolved by adding this:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    if let window = view.window,
        let recognizers = window.gestureRecognizers {
        recognizers.forEach { r in
            r.delaysTouchesBegan = false
            r.cancelsTouchesInView = false
            r.isEnabled = false
        }
    }
}

Snippet from: https://mcmap.net/q/1474302/-touchesbegan-withevent-is-delayed-at-left-edge-of-screen

Requisite answered 21/8 at 4:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.