Search code examples
iosswiftcustom-keyboard

iOS Custom Keyboard Extension - Change height to fullscreen


I have an iOS Custom Keyboard Extension (UIInputViewController) and I want to make it fullscreen.

Changing its height is pretty straightforward:

self.heightConstraint = NSLayoutConstraint(item: self.view!,
                                                   attribute: NSLayoutConstraint.Attribute.height,
                                                   relatedBy: NSLayoutConstraint.Relation.equal,
                                                   toItem: nil,
                                                   attribute: NSLayoutConstraint.Attribute.notAnAttribute,
                                                   multiplier: 0,
                                                   constant: UIScreen.main.bounds.height)

The problem is that the keyboard's frame is placed on top of the UIKeyboardDockView -- the bottom area that contains the globe (change keyboard) and the mic (dictation) icon.

Now, I want to get access to that bottom area so that I can subtract its height to UIScreen.main.bounds.height.

Does anyone know if it's possible? If not, please feel free to suggest any other solution.

Xcode UI debugger view Simulator screenshot


Solution

  • This is working for me. It looks like calling view.layoutIfNeeded() inside viewWillAppear is important.

    import UIKit
    
    class InputVC: UIInputViewController {
        
        var initialHeightConstraint: NSLayoutConstraint?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            guard let view = view else { return }
            
            view.backgroundColor = .red
            
            view.translatesAutoresizingMaskIntoConstraints = false
            
            self.initialHeightConstraint = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: UIScreen.main.bounds.height)
            self.initialHeightConstraint?.isActive = true
        }
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            
            view.layoutIfNeeded()
        }
    }
    
    class ViewController: UIViewController {
        
        var inputVC = InputVC()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            view.backgroundColor = .green
        }
        
        override var canBecomeFirstResponder: Bool {
            true
        }
        
        override var inputAccessoryViewController: UIInputViewController? {
            inputVC
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            
            becomeFirstResponder()
        }
    }
    

    I also tried pinning it to the window in a few spots, which also worked, but gave me constraint errors in the console.

    override func didMove(toParent parent: UIViewController?) {
        super.didMove(toParent: parent)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        guard let view = view, let window = view.window else { return }
    
        self.initialHeightConstraint?.isActive = false
        
        NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: window, attribute: .top, multiplier: 1, constant: 0).isActive = true
        
        NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal, toItem: window, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
        
        NSLayoutConstraint(item: view, attribute: .left, relatedBy: .equal, toItem: window, attribute: .left, multiplier: 1, constant: 0).isActive = true
    
        NSLayoutConstraint(item: view, attribute: .right, relatedBy: .equal, toItem: window, attribute: .right, multiplier: 1, constant: 0).isActive = true
    
        self.view.layoutIfNeeded()
    }