【Swift】TextField全選択時の処理で「2回に1度しか全選択の状態にならない」場合の対処方法

# はじめに
前回掲載した [【Swift】 TextFieldをタップした時に全選択できるようにする](https://qiita.com/_CHUBURA/items/014aa54275b5c40649dd) において、 
不具合を見つけたため、今回はその対処方法を載せます。

# 事象
## 2回に1度しか全選択の状態にならない
1回目にTextFieldをタップした時は全選択ができているが、再度繰り返し同じ動作をすると全選択とならない。
### 再現
1. シュミレータ起動後、対象のTextFieldを選択(タップ)する。
2. キーボードが上がり、TextFieldが全選択される。
3. 確認後、TextField以外の部分を選択し、TextFieldの選択状態を解除する(キーボードが下がった状態)。
4. 再度、対象のTextFieldを選択(タップ)する。
5. TextField上で全選択がされず、通常の選択状態となる or 選択動作が上手くされない

# 原因
## 原因のコード
### 1. TextFieldの選択範囲を指定する方法
```swift:ViewController.swift
func textFieldDidBeginEditing(_ textField: UITextField) {

    // TextFieldの選択範囲を指定する
    textField.selectedTextRange = textField.textRange(
        from: textField.beginningOfDocument,
        to: textField.endOfDocument)
}
```
### 2. TextField内の文字列を選択状態にする方法
```swift:ViewController.swift
func textFieldDidBeginEditing(_ textField: UITextField) {

    // nilチェック
    if textField != self.urlTextField {
        return
    }

    // TextField内の文字列を選択状態にする
    textField.selectAll(self.textField.text)
}
```

# 対処方法
調べてみたら、以下の同事象と似ている記事を見つけました。
> stackoverflowより
>
> - [selectall uitextfield does not always select all
](https://stackoverflow.com/questions/12666834/selectall-uitextfield-does-not-always-select-all)
> - [objective c textField: selectAll text doesn't always work
](https://stackoverflow.com/questions/18794293/objective-c-textfield-selectall-text-doesnt-always-work?rq=1)

これによると、
遅延処理を加えると解消されるとあったので、実際に試してみる

## 遅延処理について
遅延処理を加える実装方法(以下3つ)とその参考記事を簡単にまとめてみました。

- `performSelector: withObject: afterDelay:`
    - [処理を遅延実行／遅延処理する方法【Swift/Objective-C】](http://marycore.jp/prog/objective-c/timer-delay/)
    - [Alternative to performSelector in Swift?](https://stackoverflow.com/questions/24158427/alternative-to-performselector-in-swift)
    - [Swift performSelector: withObject: afterDelay: [duplicate]
](https://stackoverflow.com/questions/24170282/swift-performselector-withobject-afterdelay)
- `dispatch_after`
    - [メソッドの遅延実行はperformSelectorでなくdispatch_afterを使う](http://hachinobu.hateblo.jp/entry/2014/02/06/200229)
    - [Swiftでの遅延実行](https://qiita.com/itoru257/items/ee3150d2ba25aa013d7d)
- `DispatchQueue.main.asyncAfter(deadline: )`
    - [Swift3で一定時間後に処理を実行する方法](http://swift.tecc0.com/?p=669)
    - [[Swift 3] Swift 3時代のGCDの基本的な使い方](https://dev.classmethod.jp/smartphone/iphone/swift-3-how-to-use-gcd-api-1/)

色々記事を見た結果、
今回はSwift3以降で使用可能となった`dispatch_after`を適用させてみようと思います。

### 遅延処理`dispatch_after`の適用
#### 1.の「TextFieldの選択範囲を指定する方法」に適用する

```swift:ViewController.swift
func textFieldDidBeginEditing(_ textField: UITextField) {

    // 遅延処理 → 0.1秒後に実行
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        // TextFieldの選択範囲を指定する
        textField.selectedTextRange = textField.textRange(
            from: textField.beginningOfDocument,
            to: textField.endOfDocument)
    }
}
```
#### ２.の「TextField内の文字列を選択状態にする方法」に適用する
```swift:ViewController.swift
func textFieldDidBeginEditing(_ textField: UITextField) {

    // nilチェック
    if textField != self.urlTextField {
        return
    }
    
    // 遅延処理 → 0.1秒後に実行
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        // TextField内の文字列を選択状態にする
        textField.selectAll(self.textField.text)
    }
}
```

修正後、「2回に1度しか全選択の状態にならない」が解消され、
TextField選択時に全選択されるようになった。