質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.80%

utf16において1キャラクターの現し方(ボタンによる絵文字の削除方法)

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,004

score 98

やりたいこと

カーソルの左の文字をボタンで削除する場合1回のタップで絵文字を削除したい。

問題点

コーディングをして半角英数字、全角日本文字は削除できますが、絵文字を削除する場合、文字により2回か3回タップする必要が発生しています。

考察と質問

下記のコードにおいて
//1文字のスペース
let amountOfCharacter = 1
の右辺の1をutf16で表現できれば解決すると思いました。
解決策を教えてください。

コード

        print("ここまで来た08b")

        //guard は条件を満たしていない時に、迅速にスコープを抜けるための処理(breakやreturn、例外のthrowなど)を行う。
        //テキストがない場合抜ける

        guard let fullText = InputView.text else {
            return

        }

        print("ここまで来た08c")

        //カーソルの位置を定義
        let offset = InputView.selectedRange.location

        //文字列が"あいうえお"の時、キャレットが"あ"の左側にあるならばoffset == 0
        //キャレットが一番左にある場合削除する文字がないので抜ける
        if offset <= 0 {
            return
        }
        print("ここまで来た08d")
        //カーソルの左の文字列
        let startIndex = fullText.utf16.startIndex
        let endIndex = fullText.utf16.endIndex

        //1文字のスペース⇒ ◎◎◎ ここが問題
        let amountOfCharacter = 1

        //カーソルの左の1文字を削除するこのコードをCommentOutしても同じ結果になった
       // InputView.deleteBackward()
        print("ここまで来た08e")

        //カーソルの左より1文字削除後の左の文字列の順位
        let targetOfLeftIndex = fullText.utf16.index(startIndex, offsetBy: offset - amountOfCharacter)
        //数字の1を入れたら同じ現象になった
    //let targetOfLeftIndex = fullText.utf16.index(startIndex, offsetBy: offset - 1)
        //カーソルの右側の文字列の順位
        let targetOfRightIndex = fullText.utf16.index(startIndex, offsetBy: offset)
        //右側の文字列
        let rightText = fullText[targetOfRightIndex..<endIndex]
        //左の削除後の文字列定義(文字列の順位で設定)
        //let remain = fullText.underestimatedCount - String(rightText).underestimatedCount - amountOfCharacter
        //左側の文字列
        //let leftText = fullText.suffix(remain)

        let leftText = fullText[startIndex..<targetOfLeftIndex]
        //右側の文字列
        //let rightText = fullText[targetOfRightIndex..<endIndex]
        //左右の文字列を並べた全文字列
        let modifiedText = leftText + rightText
        //左の削除後の全文字列の表示
        InputView.text = String(modifiedText)

        //カーソルの位置表示
        InputView.selectedRange.location = offset-amountOfCharacter
        }

ご参考事項

  • 環境は下記のとおりです。
    MacBook Pro (15-inch, 2016)
    High Sierra OS10.13
    Version 9.1 (9B55)Swift4にバージョンアップしました。
  • 下記のように定義しています。
    @IBOutlet weak var InputView: UITextView!
    @IBOutlet weak var Sakujo01: UIButton!
  • 画面は下記のとおりです。最初はキーボードはでておらず画面をタップして文字記入後削除ボタンを押して文字を消したいと思っております。画面のカーソルの左は削除後Aが現れもう一度タップすると消えます。
    イメージ説明

よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

いつだったかのコメント欄にすでに書いていますが、左側だけならシンプルに

@IBAction func deleteLeftCharacter(sender: UIButton) {
    InputView.deleteBackward()
}

とやる選択肢もあります。ちなみにInputView.deleteForward()は無いので実装の必要がありました。

一からコードを書くならば、やることを反転させて

@IBAction func deleteLeftCharacter(_ sender: UIBarButtonItem) {

        guard let fullText = textView.text else {
            return
        }

        let location = textView.selectedRange.location
        let count = fullText.utf16.count
        if location <= 0 {
            return
        }
        let offset = location - count

        let endIndex = fullText.utf16.endIndex

        let cursorIndex = fullText.utf16.index(endIndex, offsetBy: offset)
        let rightText = fullText[cursorIndex..<endIndex]

        let amountOfFullTextCharacter = fullText.underestimatedCount
        let amountOfRightTextCharacter = String(rightText).underestimatedCount
        let amountOfDeleteCharacter = 1
        let remain = amountOfFullTextCharacter - amountOfRightTextCharacter - amountOfDeleteCharacter

        let leftText = fullText.prefix(remain)

        let modifiedText = leftText + rightText

        textView.text = String(modifiedText)

        textView.selectedRange.location = leftText.utf16.count

    }

こうなります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/26 09:56

    コメントありがとうございます。励みになります。その後のご報告します。以前「もどる」ボタンを考えていましたが、Undo is astonishingly hard. という記述があるのを見つけましたので断念しました。また、現在Mail、メモ、LINE、Google検索のアプリを起動する機能も難しいのでひとつくらいに絞りバージョンアップの時に増やそうかと思っています。また、広告を入れるとスペースが狭すぎるのでボタンの個数を一行7個に増やして2列にしようかと思っています。いずれバナーをやめてスペースを食わない広告も勉強する必要があります。
    ご紹介頂いた「リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック 」はAmazonの到着待ちです。今後ともご指導ください。
    今日はこれからSwift in Yokohamaのもくもく会に行き最後の仕上げをしたいと思っています。全部完成したら質問の編集機能で報告をしたいと思います。

    キャンセル

  • 2017/11/26 10:00

    xAxisさんへ 体調をくずされたとのことですが、大事になさってください、
    そういう時にまでコメント本当にありがとうございます。
    広島でご披露させて頂きます。

    キャンセル

  • 2017/11/29 19:40

    遅くなってしまったこと、そして質問依頼を頂いていたのに反応できずにすいませんでした。

    > Undo is astonishingly hard.
    astonishinglyという表現は知らなかったので調べてみたら強めの表現なのですね。
    確かにUndoやRedoの実装はかなり大変そうに思います。。。
    こちらでも調べてみたのですが関数型プログラミングで行う方法、デザインパターンを利用して行う方法、気合いで実装する方法等あるようですが、
    前二つは学習コストがかかること、気合いで実装するにはロジックを考えるのがかなり大変だと思います。

    多機能だとユーザ側としても嬉しい反面、1画面が持つ情報量が多すぎるとユーザが情報を処理出来なくなるということもありますし次回のバージョンアップへ持ち越しもありだと思います。

    > 今後ともご指導ください。
    僕の方こそ新たな知見を得られることはもちろん、Tomzyさんがお持ちの大きな意欲に大変刺激を受けていますから指導なんて恐れ多いですwですがもし私が答えられる範囲でしたらご遠慮なくどうぞ。微力ながら尽力致します。

    お気遣いの言葉ありがとうございます。嬉しく思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る