KeyboardExtension使用emojis调整TextTosition问题

时间:2017-10-30 13:17:34

标签: ios swift4 ios-keyboard-extension

我正在帮助构建一个keyboardextension,我最近遇到了Swift 4和emojis的问题。对Swift 4的新UTF-16表情符号支持非常好,但adjustTextPosition中的UIInputViewController存在问题。

如果我们调用adjustTextPosition来跳过一个表情符号,它就不会走得太远,看起来UIInputViewController使用的字符偏移量与系统使用的字符数不匹配。

要测试只需用emojis写一个文本,只要点击某个键,就可以调用:

super.textDocumentProxy.adjustTextPosition(byCharacterOffset: 1)

可以观察到的是,我们必须点击它超出预期。

4 个答案:

答案 0 :(得分:1)

Swift 5,以下代码似乎在iOS 12上运行良好。

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script async defer
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
    </script>
 <style>
       /* Set the size of the div element that contains the map */
      #map {
        height: 400px;  /* The height is 400 pixels */
        width: 100%;  /* The width is the width of the web page */
       }
    </style>
</head>
<body>

<div class="container">
  <h2>Modal Example</h2>
  <!-- Button to Open the Modal -->
  <!-- add attribute data-lat="5.134515" data-long="97.151759" to pass location data -->
  <button type="button" id="open" class="btn btn-primary" data-toggle="modal" data-target="#myModal" data-lat="5.134515" data-long="97.151759">
    Open modal
  </button>

  <!-- The Modal -->
  <div class="modal" id="myModal">
    <div class="modal-dialog">
      <div class="modal-content">

        <!-- Modal Header -->
        <div class="modal-header">
          <h4 class="modal-title">Modal Heading</h4>
          <button type="button" class="close" data-dismiss="modal">&times;</button>
        </div>

        <!-- Modal body -->
        <div class="modal-body">
           <!-- show map here -->
            <div id="map"></div>
        </div>

        <!-- Modal footer -->
        <div class="modal-footer">
          <button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
        </div>

      </div>
    </div>
  </div>

</div>
<script>
   function init(myLoc) {
        var marker = new google.maps.Marker({
            position: myLoc
        });
      var opt = {
            center: myLoc,
            zoom: 13,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        map = new google.maps.Map(document.getElementById("map"), opt);
        marker.setMap(map);
    };

$("#open").click(function(){
// get latitude and longitude that pass from open modal button
    init(new google.maps.LatLng($(this).data('lat'), $(this).data('long')));
    });

</script>
</body>
</html>

答案 1 :(得分:0)

似乎不同的应用程序使用不同的步进技术。因此,如果需要使用adjustTextPosition,键盘扩展将很难在多个不同的应用程序中处理表情符号。

答案 2 :(得分:0)

调整在字素簇(雨燕字符)中测量的插入符号位置:

func adjustCaretPosition(offset: Int) {
    guard let textAfterCaret = textDocumentProxy.documentContextAfterInput else { return }
    
    if let offsetIndex = offset > 0 ?  textAfterCaret.index(textAfterCaret.startIndex, offsetBy: offset, limitedBy: textAfterCaret.endIndex) : textBeforeCaret.index(textBeforeCaret.endIndex, offsetBy: offset, limitedBy: textAfterCaret.startIndex),
        let offsetIndex_utf16 = offsetIndex.samePosition(in: offset > 0 ? textAfterCaret.utf16 : textBeforeCaret.utf16)
    {
        let offset = offset > 0 ? textAfterCaret.utf16.distance(from: textAfterCaret.utf16.startIndex, to: offsetIndex_utf16) : textBeforeCaret.utf16.distance(from: textBeforeCaret.utf16.endIndex, to: offsetIndex_utf16)
        textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
    }
    else {
        textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
    }
}

UPD:一个混乱的hack,用于尝试解决Safari不一致问题。由于无法区分野生动物园和野生动物园,因此基于结果采取行动似乎是唯一的解决方案。

func adjustCaretPosition(offset: Int) {
    // for convenience
    let textAfterCaret = textDocumentProxy.documentContextAfterInput ?? ""
    let textBeforeCaret = textDocumentProxy.documentContextBeforeInput ?? ""
    
    if let offsetIndex = offset > 0 ?  textAfterCaret.index(textAfterCaret.startIndex, offsetBy: offset, limitedBy: textAfterCaret.endIndex) : textBeforeCaret.index(textBeforeCaret.endIndex, offsetBy: offset, limitedBy: textAfterCaret.startIndex),
        let offsetIndex_utf16 = offsetIndex.samePosition(in: offset > 0 ? textAfterCaret.utf16 : textBeforeCaret.utf16)
    {
        // part of context before caret adjustment
        let previousText = offset > 0 ? textAfterCaret : textBeforeCaret
        
        // what we expect after adjustment
        let expectedText = offset > 0 ? String(textAfterCaret[offsetIndex..<textAfterCaret.endIndex]) : String(textBeforeCaret[textBeforeCaret.startIndex..<offsetIndex])
        
        // offset in UTF-16 characters
        let offset_utf16 = offset > 0 ? textAfterCaret.utf16.distance(from: textAfterCaret.utf16.startIndex, to: offsetIndex_utf16) : textBeforeCaret.utf16.distance(from: textBeforeCaret.utf16.endIndex, to: offsetIndex_utf16)
        
        // making adjustment
        textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
        
        // part of context after caret adjustment
        let compareText = offset > 0 ? textAfterCaret : textBeforeCaret
        
        // rollback if got unwanted results
        // then adjust by grapheme clusters offset
        if compareText != "", expectedText != compareText, compareText != previousText {
            textDocumentProxy.adjustTextPosition(byCharacterOffset: -offset_utf16)
            textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
        }
    }
    else {
        // we probably stumbled upon a textDocumentProxy inconsistency, i.e. context got divided by an emoji
        // adjust by grapheme clusters offset
        textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
    }
}

答案 3 :(得分:0)

试试这个

let correctedOffset = adjust(offset: offset)
textDocumentProxy.adjustTextPosition(byCharacterOffset: correctedOffset)

private func adjust(offset: Int) -> Int {
    if offset > 0, let after = textDocumentProxy.documentContextAfterInput {
        let offsetStringIndex = after.index(after.startIndex, offsetBy: offset)
        let chunk = after[..<offsetStringIndex]
        let characterCount = chunk.utf16.count
        return characterCount
        
    } else if  offset < 0, let before = textDocumentProxy.documentContextBeforeInput {
        let offsetStringIndex = before.index(before.endIndex, offsetBy: offset)
        let chunk = before[offsetStringIndex...]
        let characterCount = chunk.utf16.count
        return -1*characterCount
    } else {
        return offset
    }
}