Search code examples
iosswiftuikeyboard

How to show the standard number keyboard without a UITextField in Swift on viewDidLoad


I'm trying to show the standard's number keyboard when the user gets to my ViewController, and be able to capture user's input to fill some circles that are on the UI each time the user enters a number, the circles in question are the ones I show on this question.

enter image description here

How can I do that? All the question that I've already read suggest using UIKeyInput protocol to display a custom keyboard, but that's not what I'm trying to do. One of those is this one.

The code I came up with, after checking the above post, was this one, but after a lot of reading and discussing, this was for having the above's UIView as my custom keyboard and I'm unsure how to make it right.


ViewController

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var dotsView: DotsView!
    
    var keyInputView: KeyInputView = KeyInputView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        keyInputView.inputView = dotsView
        keyInputView.becomeFirstResponder()
        //self.view.addSubview(keyInputView) //In the XIB, the view is set to be displayed in center vertically and horizontally. If we uncomment this line, the view is pushed downwards.
    }
}

DotsView

import UIKit

class DotsView: UIView {
    @IBOutlet var contentView: UIView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet var digitDots: [CircleView]!
    
    //initWithFrame to init view from code
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
      
    //initWithCode to init view from xib or storyboard
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupView()
    }
      
    //common func to init our view
    private func setupView() {
        Bundle.main.loadNibNamed("DotsView", owner: self, options: nil)
        addSubview(contentView)
        contentView.frame = self.bounds
        contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    }
}

CircleView

import UIKit

@IBDesignable
class CircleView: UIView {
    private var shapeLayer = CAShapeLayer()
    @IBInspectable var fillColor: UIColor = .blue {
        didSet {
            shapeLayer.fillColor = fillColor.cgColor
        }
    }

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)

        configure()
    }

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

        configure()
    }

    private func configure() {
        shapeLayer.fillColor = fillColor.cgColor
        layer.addSublayer(shapeLayer)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        shapeLayer.path = UIBezierPath(ovalIn: bounds).cgPath
    }
}

KeyInputView

import UIKit

class KeyInputView: UIView {
   var _inputView: UIView?

   override var canBecomeFirstResponder: Bool { return true }
   override var canResignFirstResponder: Bool { return true }

   override var inputView: UIView? {
       set { _inputView = newValue }
       get { return _inputView }
   }
}

// MARK: - UIKeyInput
//Modify if need more functionality
extension KeyInputView: UIKeyInput {
    var hasText: Bool {
        return false
    }
    
    func insertText(_ text: String) {
        
    }
    
    func deleteBackward() {
        
    }
}

As for the XIBs you can access them in this repo


Solution

  • You probably want to make your DotsView conform to UIKeyInput...

    class DotsView: UIView, UIKeyInput {
        
        @IBOutlet var contentView: UIView!
        @IBOutlet weak var titleLabel: UILabel!
        @IBOutlet var digitDots: [CircleView]!
        
        //initWithFrame to init view from code
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupView()
        }
          
        //initWithCode to init view from xib or storyboard
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setupView()
        }
          
        //common func to init our view
        private func setupView() {
            Bundle.main.loadNibNamed("DotsView", owner: self, options: nil)
            addSubview(contentView)
            contentView.frame = self.bounds
            contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        }
    
        var keyboardType: UIKeyboardType {
            get {
                return .numberPad
            }
            set {}
        }
    
        // required by UIKeyInput protocol
        var hasText: Bool = false
    
        override var canBecomeFirstResponder: Bool {
            true
        }
        
        func insertText(_ text: String) {
            print("Number tapped:", text)
            // do something with the number that was tapped
        }
        
        func deleteBackward() {
            print("Delete Backward tapped")
            // do something because Delete was tapped
        }
    }
    

    Then change your viewDidLoad() in ViewController to:

    override func viewDidLoad() {
        super.viewDidLoad()
        dotsView.becomeFirstResponder()
    }
    

    Now, you have no need for an additional KeyInputView.