読者です 読者をやめる 読者になる 読者になる

【swift】UITableViewをタップされたらキーボードを隠す(けど画面遷移はしない)方法メモ

はじめに

タイトルがわかりずらいんですけど、やりたかったこととしては以下

画面としては、UISearchBarがあってUITableViewがあって、UISearchBarを編集中に画面をタップしたらキーボードを閉じる。
けど、素直に実装するとUITableViewのセルがタップされたときに「didSelectRowAtIndexPath」が実行されてしまい画面遷移なりをしてしまうのでそれは回避したい。
ということです

f:id:yoppy0066:20151113015617p:plain

概要

強引でバットノウハウな気がして仕方ないのですが、やるしかなかったので無理やり実装しました。

当初は
didSelectRowAtIndexPathが呼ばれるときに、キーボード編集中であれば何もしない
っていう処理を考えていたのですが、handleKeyboardWillHideNotification(キーボードが非表示になるときによばれる)が先によばれてしまうのでどうにもうまくいかず、困っていました。

で、検索してもわからなかったので以下のような形で実装しました

1. キーボードが表示される際に、編集中フラグを立てるおう
2. キーボード以外の画面をタップされる
3. resignFirstResponderメソッドを呼んでキーボードを閉じる
4. handleKeyboardWillHideNotificationメソッドがよばれる
ここで1秒後に編集中フラグを落とすようなタイマー処理を実行
5. didSelectRowAtIndexPathがよばれる
4で1秒後に編集中フラグを落としてるのでこの時点ではまだ編集中フラグ = ONの状態
編集中フラグ = ONの場合はなにもしない

実装

ソースそのままですが、そのままのほうが後々わかりやすいので、、、

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var tableView: UITableView!
    var searchBar: UISearchBar!
    
    var tableData: [String]!
    
    var searchbarEditing = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // データを初期化します
        tableData = ["データ1", "データ2", "データ3", "データ4", "データ5", "データ6", "データ7", "データ8", "データ9", "データ10"]
        
        // VIEWをセットします
        setView()
    }
    
    // VIEWをセットします
    func setView() {
        
        let tap = UITapGestureRecognizer(target: self, action: "onTap:");
        tap.numberOfTapsRequired = 1;
        tap.cancelsTouchesInView = false
        view.addGestureRecognizer(tap);
        
        self.navigationController?.navigationBar.barTintColor = UIColor.whiteColor()
        self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
        
        let statusBarHeight: CGFloat = UIApplication.sharedApplication().statusBarFrame.height
        let navigationBarHeight: CGFloat = (self.navigationController?.navigationBar.frame.size.height)!
        
        searchBar = UISearchBar(frame: CGRectMake(0, statusBarHeight + navigationBarHeight, self.view.frame.size.width, 50))
        searchBar.placeholder = "検索条件を入力してください"
        self.view.addSubview(searchBar)
        
        tableView = UITableView(frame: CGRect(x:0, y: searchBar.frame.origin.y + searchBar.frame.size.height, width:self.view.frame.size.width, height: self.view.frame.size.height - (statusBarHeight + navigationBarHeight + searchBar.frame.size.height)))
        tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        tableView.delegate = self
        tableView.dataSource = self
        self.view.addSubview(tableView)
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        
        let notificationCenter = NSNotificationCenter.defaultCenter()
        notificationCenter.addObserver(self, selector: "handleKeyboardWillShowNotification:", name: UIKeyboardWillShowNotification, object: nil)
        notificationCenter.addObserver(self, selector: "handleKeyboardWillHideNotification:", name: UIKeyboardWillHideNotification, object: nil)
        self.tableView.reloadData()
    }
    
    override func viewDidDisappear(animated: Bool) {
        super.viewDidDisappear(animated)
        
        let notificationCenter = NSNotificationCenter.defaultCenter()
        notificationCenter.removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        notificationCenter.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }
    
    // キーボードが表示されるとき
    func handleKeyboardWillShowNotification(notification: NSNotification) {
        self.searchbarEditing = true
        searchBar.showsCancelButton = true
    }
    
    // キーボードが非表示になるとき
    func handleKeyboardWillHideNotification(notification: NSNotification) {
        if self.searchbarEditing {
            NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "onUpdate:", userInfo: nil, repeats: false)
        }
        searchBar.showsCancelButton = false
    }
    
    // 編集中フラグを戻す
    func onUpdate(timer: NSTimer) {
        self.searchbarEditing = false
    }
    
    // 画面がタップされたらキーボードをおろす
    func onTap (recognizer:UIPanGestureRecognizer){
        self.searchBar.resignFirstResponder()
    }
    
    // テーブルセルの高さをかえします
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return 60
    }
    
    // テーブルの行数をかえします
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.tableData.count
    }
    
    // テーブルセルにデータをセットします
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
        cell.textLabel?.text = self.tableData[indexPath.row]
        cell.selectionStyle = UITableViewCellSelectionStyle.None
        return cell
    }
    
    // テーブルセル選択時の処理を記述します
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        if !self.searchbarEditing {
            let viewController: DetailViewController = DetailViewController()
            self.navigationController?.pushViewController(viewController, animated: true)
        }
    }
}

以上です