【Swift】カスタムキーボードで削除ボタンを実装する


カスタムキーボードで、削除ボタン(バックスペースボタン)を実装しようといろいろ調べてみると、単純にaddTargetで削除処理を呼び出せばいいというわけではなく、削除ボタンを押し続けた際の動きなどいろいろ考慮しないといけないようで、少しSwift初心者にはハードルが高そうだ。

ネット上にいいサンプルコードがないか探してみたところ、GitHubにうってつけのコードを発見。

https://github.com/archagon/tasty-imitation-keyboard

英語キーボードを全てコードで実装しているらしい(すごい)。各アイコンもベジエ曲線などを使いを見事に表現している。

とりあえず、削除の処理の部分のみを抜粋し、自分のプロジェクトに適用してみる。

※全てKeyboardViewController内に記述。

まずは各種プロパティを定義。

    @IBOutlet var backspaceButton: UIButton!
    let backspaceDelay: NSTimeInterval = 0.5
    let backspaceRepeat: NSTimeInterval = 0.07
    var backspaceActive: Bool {
        get {
            return (backspaceDelayTimer != nil) || (backspaceRepeatTimer != nil)
        }
    }
    var backspaceDelayTimer: NSTimer?
    var backspaceRepeatTimer: NSTimer?

次にdeinitを。理由はよくわからないが、NSNotificationCenterの解除はdeinitで行うらしい。

    deinit {
        backspaceDelayTimer?.invalidate()
        backspaceRepeatTimer?.invalidate()
        
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

次に、viewDidLoad内で、backspaceButtonをAutoLayoutを使ってviewに設置する。

    override func viewDidLoad() {
        super.viewDidLoad()
        …省略
        //削除ボタン
        self.backspaceButton = UIButton.buttonWithType(.System) as! UIButton
        self.backspaceButton.setTitle("削除", forState: .Normal)
        self.backspaceButton.sizeToFit()
        self.backspaceButton.setTranslatesAutoresizingMaskIntoConstraints(false)
        let cancelEvents: UIControlEvents = UIControlEvents.TouchUpInside|UIControlEvents.TouchUpInside|UIControlEvents.TouchDragExit|UIControlEvents.TouchUpOutside|UIControlEvents.TouchCancel|UIControlEvents.TouchDragOutside
        
        self.backspaceButton.addTarget(self, action: "backspaceDown:", forControlEvents: .TouchDown)
        self.backspaceButton.addTarget(self, action: "backspaceUp:", forControlEvents: cancelEvents)
        
        self.view.addSubview(self.backspaceButton)
        
        //AutoLayoutを指定するときこれは必ず記述
        self.backspaceButton.setTranslatesAutoresizingMaskIntoConstraints(false)
        var backspaceButtonRightSideConstraint = NSLayoutConstraint(item: self.backspaceButton, attribute: .Right, relatedBy: .Equal, toItem: self.view, attribute: .Right, multiplier: 1.0, constant: 0.0)
        var backspaceButtonBottomConstraint = NSLayoutConstraint(item: self.backspaceButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: 0.0)
        self.view.addConstraints([backspaceButtonRightSideConstraint, backspaceButtonBottomConstraint])
        …省略
    }

最後に各種メソッドを定義。

    func cancelBackspaceTimers() {
        self.backspaceDelayTimer?.invalidate()
        self.backspaceRepeatTimer?.invalidate()
        self.backspaceDelayTimer = nil
        self.backspaceRepeatTimer = nil
    }
    
    func backspaceDown(sender: UIButton) {
        self.cancelBackspaceTimers()
        
        if let textDocumentProxy = self.textDocumentProxy as? UIKeyInput {
            textDocumentProxy.deleteBackward()
        }
        
        // trigger for subsequent deletes
        self.backspaceDelayTimer = NSTimer.scheduledTimerWithTimeInterval(backspaceDelay - backspaceRepeat, target: self, selector: Selector("backspaceDelayCallback"), userInfo: nil, repeats: false)
    }
    
    func backspaceUp(sender: UIButton) {
        self.cancelBackspaceTimers()
    }
    
    func backspaceDelayCallback() {
        self.backspaceDelayTimer = nil
        self.backspaceRepeatTimer = NSTimer.scheduledTimerWithTimeInterval(backspaceRepeat, target: self, selector: Selector("backspaceRepeatCallback"), userInfo: nil, repeats: true)
    }
    
    func backspaceRepeatCallback() {
        
        if let textDocumentProxy = self.textDocumentProxy as? UIKeyInput {
            textDocumentProxy.deleteBackward()
        }
    }

これで、ビルドすれば「削除」と書かれたボタンが右下に設置され、タップすると削除処理が実行されるだろう。
とりあえずシミュレータ上ではうまく動いているようだ。


投稿者: しんじ

ベトナムでオフショア開発会社経営中 www.bit-vietnam.com 。サービス開発が趣味。web系は主にPHPで、スマホアプリはswiftで。最近はエンジニア向けの英語勉強webアプリ エングリッシュ e-lish.io を作りました。