ばろぐ・ほいダウン

2010年Nゲージにより鉄道事業を開始。2011年Gゲージに参入することで事業拡大。常に夢と目標と野望を持ち活動していく。

MENU

SwiftでUIAlertControllerにUITextViewを追加し、キーボードを閉じられるようにする

iOSアプリ開発を進めるにあたって、UIAlertControllerにUITextViewを追加する必要が出てきた。
UITextFieldなら数行のコードを追加するだけで良いのだが、UITextViewは少し苦労する。

特にキーボードを閉じる処理において凡ミスをやらかしてて時間を食ったので、将来同じミスをしないよう自分に向けてもこの記事を書いておく。

動きとしてはこんな感じ

全体のコード

UIAlertControllerにUITextViewを追加

ViewControllerにStoryboard上のUITextView(以下memoTextView)を接続し、memoTextViewタップするとUIAlertControllerが開くようになっている。



各部を見ていく

viewDidLoad

delegateもtagをつけるのも忘れないように



touchesBegan

memoTextViewがタップされた時の処理。
isTouchという関数を呼び出し、コンソールに「TextView touch!!」と表示されるようになっている。
memoTextViewのEditableとSelectableはオフにしておく。



関数 isTouch

タグのついたmemoTextViewがタップされた時の動作をここで書いていく。

let margin:CGFloat = 8.0 
let rect = CGRect(x: margin, y: margin, width: 255, height: 150.0)
let customView = UITextView(frame: rect)
customView.backgroundColor = UIColor.white
customView.textColor = .black
customView.font = UIFont(name: "Helvetica", size: 16)
customView.text = memoTextView.text
customView.delegate = self

この部分でUIAlertControllerに追加するためのUITextView(customView)について書いている。
widthやheightはこのサイズにしておくとどの機種でも良い感じに表示されると思う。

ツールバーに完了ボタンの追加

テキストを入力後キーボードを閉じられるようにする

// ツールバー生成
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 320, height: 40))
// スタイルを設定
toolBar.barStyle = UIBarStyle.default
// 画面幅に合わせてサイズを変更
toolBar.sizeToFit()
// 閉じるボタンを右に配置
let spacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: self, action: nil)
// 閉じるボタン
let commitButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: self, action: #selector(commitButtonTapped))
// スペース、閉じるボタンを右側に配置
toolBar.items = [spacer, commitButton]
// textViewのキーボードにツールバーを設定
customView.inputAccessoryView = toolBar

ボタンが押された時にcommitButtonTappedという関数を呼び出す。


最後に

alertController.view.addSubview(customView)

でUIAlertControllerにcustomViewを追加したら、うまく表示されるようになっている。



関数 commitButtonTapped

@objc func commitButtonTapped() {
          self.view.endEditing(true)
      }

最初はこのように書いていたが、キーボードは閉じられなかった。
理由はもともとのviewの上にUIAlertControllerが表示されるような構造であるため、このコードは最前面にあるUIAlertControllerには効かなかったから。

 @objc func commitButtonTapped() {
        if var topController = UIApplication.shared.keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }
            topController.view.endEditing(true)
        }
        
    }

このように最前面にあるUIAlertControllerを取得することで解決する。


タッチイベントのメソッド

class ToucheEventTextView: UITextView {
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesBegan(touches , with: event)
        } else {
            super.touchesBegan(touches , with: event)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesEnded(touches , with: event)
        } else {
            super.touchesEnded(touches , with: event)
        }
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesCancelled(touches, with: event)
        } else {
            super.touchesCancelled(touches, with: event)
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesMoved(touches, with: event)
        } else {
            super.touchesMoved(touches, with: event)
        }
    }
}

これがないといくらmemoTextViewをタップしても動かない。
一覧にしてあるが、今回使っているのはtouchesBeganのみ。



参考サイト

[Swift, iOS] UITextViewをタップしたときのイベントを拾いたい - TERAKOYA