回答編集履歴
2
加筆・修正
    
        answer	
    CHANGED
    
    | @@ -1,3 +1,7 @@ | |
| 1 | 
            +
            ###前書き
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            これは絵文字のことを**考慮していない**アプローチです。絵文字を考慮したものは回答の最下部に書いてみました。
         | 
| 4 | 
            +
             | 
| 1 5 | 
             
            ###実装の流れ
         | 
| 2 6 |  | 
| 3 7 | 
             
            今回実装したい機能の流れとしては
         | 
| @@ -80,4 +84,77 @@ | |
| 80 84 | 
             
                    //(5)
         | 
| 81 85 | 
             
                    let modifiedText = leftText + rightText
         | 
| 82 86 | 
             
                    textView.text = String(modifiedText)
         | 
| 87 | 
            +
            ```
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            ###絵文字が混ざった場合
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            ```swift
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    /*
         | 
| 94 | 
            +
                     事前情報
         | 
| 95 | 
            +
                     
         | 
| 96 | 
            +
                     今回絵文字が含まれているのでUTF16に関する知識が多少必要になり、さらにアプローチが上記のものとは部分的に変わっています。
         | 
| 97 | 
            +
                     */
         | 
| 98 | 
            +
                    
         | 
| 99 | 
            +
                    /*
         | 
| 100 | 
            +
                     アプローチについて
         | 
| 101 | 
            +
                     
         | 
| 102 | 
            +
                     絵文字を考慮しつつカーソルの右側を削除する場合、考えることは
         | 
| 103 | 
            +
                     1.入力された全ての文字列
         | 
| 104 | 
            +
                     2.startIndexからカーソルまでの文字列
         | 
| 105 | 
            +
                     3.削除する文字数
         | 
| 106 | 
            +
                     4.カーソルの右側に残った文字列の数
         | 
| 107 | 
            +
                     5.UTF16の文字列は「あ」という文字は1文字として認識されるが「????」という文字は2文字として認識される。さらに色付きの文字だったりすると倍になったりする(詳細はUTF16, unicode等検索して下さい)
         | 
| 108 | 
            +
                     6.5番を踏まえるとカーソルの右が普通の文字であろうと絵文字であろうと対応できるコードを書かなければならない
         | 
| 109 | 
            +
                     
         | 
| 110 | 
            +
                     1.はfullText
         | 
| 111 | 
            +
                     2.はfullText[startIndex..<cursorIndex]
         | 
| 112 | 
            +
                     3.はカーソルの右側1文字だけなのでamountOfCharacter = 1
         | 
| 113 | 
            +
                     4.は実機等画面に表示されている全ての文字列の数 - leftTextの数 - ammountOfCharacter
         | 
| 114 | 
            +
                     で表します。
         | 
| 115 | 
            +
                     5.これのおかげでUITextView.index(_,offsetBy:)で隣の文字を取得するという方法が使えません。
         | 
| 116 | 
            +
                     6.なので4番で取得した文字列分fullTextから切り取る、という方法を取ります。
         | 
| 117 | 
            +
                     */
         | 
| 118 | 
            +
                    
         | 
| 119 | 
            +
                    //(1)
         | 
| 120 | 
            +
                    guard let fullText = textView.text else {
         | 
| 121 | 
            +
                        return
         | 
| 122 | 
            +
                    }
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    //UITextView.selectedRange.locationで取れる位置はUTF16で換算されているようです(要検証)
         | 
| 125 | 
            +
                    let offset = textView.selectedRange.location
         | 
| 126 | 
            +
                    let count = fullText.utf16.count
         | 
| 127 | 
            +
                    
         | 
| 128 | 
            +
                    if offset >= count {
         | 
| 129 | 
            +
                        return
         | 
| 130 | 
            +
                    }
         | 
| 131 | 
            +
                    
         | 
| 132 | 
            +
                    //(2)
         | 
| 133 | 
            +
                    //文字列の最初のインデックス情報
         | 
| 134 | 
            +
                    let startIndex = fullText.utf16.startIndex
         | 
| 135 | 
            +
                    //カーソルのインデックス情報
         | 
| 136 | 
            +
                    let cursorIndex = fullText.utf16.index(startIndex, offsetBy: offset)
         | 
| 137 | 
            +
                    //文字列の最初からカーソルまでの文字列
         | 
| 138 | 
            +
                    let leftText = fullText[startIndex..<cursorIndex]
         | 
| 139 | 
            +
                    
         | 
| 140 | 
            +
                    //(3),(4)
         | 
| 141 | 
            +
                    //文字数の把握及び計算
         | 
| 142 | 
            +
                    let amountOfFullTextCharacter = fullText.underestimatedCount
         | 
| 143 | 
            +
                    let amountOfLeftTextCharacter = String(leftText).underestimatedCount
         | 
| 144 | 
            +
                    let amountOfDeleteCharacter = 1
         | 
| 145 | 
            +
                    let remain = amountOfFullTextCharacter - amountOfLeftTextCharacter - amountOfDeleteCharacter
         | 
| 146 | 
            +
                    
         | 
| 147 | 
            +
                    //(5),(6)
         | 
| 148 | 
            +
                    //文字列の末尾からremain分切り取り
         | 
| 149 | 
            +
                    let rightText = fullText.suffix(remain)
         | 
| 150 | 
            +
                    
         | 
| 151 | 
            +
                    //文字列の結合。カーソルの左側を削除した文字列になる
         | 
| 152 | 
            +
                    let modifiedText = leftText + rightText
         | 
| 153 | 
            +
                    
         | 
| 154 | 
            +
                    //modifiedTextはsubstringでStringではないのでStringにしてUITextView.textに代入
         | 
| 155 | 
            +
                    textView.text = String(modifiedText)
         | 
| 156 | 
            +
                    
         | 
| 157 | 
            +
                    //カーソルの位置を元に戻す
         | 
| 158 | 
            +
                    textView.selectedRange.location = offset
         | 
| 159 | 
            +
                    
         | 
| 83 160 | 
             
            ```
         | 
1
修正
    
        answer	
    CHANGED
    
    | @@ -2,14 +2,14 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            今回実装したい機能の流れとしては
         | 
| 4 4 |  | 
| 5 | 
            +
            0. テキストがない場合抜ける
         | 
| 5 | 
            -
            0. カーソルの位置が一番右にある場合 | 
| 6 | 
            +
            0. カーソルの位置が一番右にある場合抜ける
         | 
| 6 | 
            -
            0. テキストがない場合も抜ける
         | 
| 7 7 | 
             
            0. ターゲットとする文字の左側のインデックスおよび右側のインデックスの取得を行う。
         | 
| 8 8 | 
             
            0. 文字列の頭からターゲットの左側までの文字列およびターゲットの右側から文字列の最後尾の二つを取得する
         | 
| 9 9 | 
             
            0. 4で取得した文字列の結合
         | 
| 10 10 |  | 
| 11 11 | 
             
            となります。
         | 
| 12 | 
            -
            やることは前回した回答と同じような流れではあるのですが | 
| 12 | 
            +
            やることは前回した回答と同じような流れではあるのですが2の場合の抜け方とtargetOfLeftIndexおよびtargetOfRightIndexの取得方法が若干異なります。
         | 
| 13 13 |  | 
| 14 14 | 
             
            ###型の違い
         | 
| 15 15 | 
             
            前回の質問の段階でもっと丁寧にしておけばと思っていたのですが今回その機会がやってきたので少し説明をしておきます。今回出てくる定数のうちstartIndex, endIndex, targetOfLefindex, targetOfRightIndexはString.indexという少々特殊な型です。が一方でoffsetは基本的なIntになります。後述してあるfullText.countも同様にIntです。型が違うのでそのままたし算、ひき算は出来ません。
         | 
| @@ -39,8 +39,8 @@ | |
| 39 39 |  | 
| 40 40 | 
             
            ###コーディングの前準備
         | 
| 41 41 |  | 
| 42 | 
            -
            前回した回答との違いは実のところ | 
| 42 | 
            +
            前回した回答との違いは実のところ2と3だけになります。
         | 
| 43 | 
            -
            前回の質問では | 
| 43 | 
            +
            前回の質問では2の部分は「カーソルの位置が一番左にある場合抜ける」でした。
         | 
| 44 44 | 
             
            そしてカーソルの位置が一番左の時を表すコードは
         | 
| 45 45 | 
             
            ```
         | 
| 46 46 | 
             
            offset == 0
         | 
| @@ -55,10 +55,12 @@ | |
| 55 55 | 
             
            ```swift
         | 
| 56 56 |  | 
| 57 57 |  | 
| 58 | 
            -
                    //( | 
| 58 | 
            +
                    //(1)
         | 
| 59 59 | 
             
                    guard let fullText = textView.text else {
         | 
| 60 60 | 
             
                        return
         | 
| 61 | 
            +
                    }
         | 
| 62 | 
            +
             | 
| 61 | 
            -
                     | 
| 63 | 
            +
                    //(2)
         | 
| 62 64 | 
             
                    let count = fullText.count
         | 
| 63 65 | 
             
                    let offset = textView.selectedRange.location
         | 
| 64 66 | 
             
                    if offset >= count {
         | 
