UITextView があり、ユーザーが絵文字を入力したかどうかを検出する必要があります。
最新の文字の Unicode 値をチェックするだけで十分だと思いますが、新しい絵文字 2 では、一部の文字が Unicode インデックス全体に散らばっています (つまり、Apple の新しく設計された著作権と登録ロゴ)。
おそらく、NSLocale または LocalizedString 値で文字の言語をチェックすることと関係があるのでしょうか?
誰か良い解決策を知っていますか?
ありがとう!
UITextView があり、ユーザーが絵文字を入力したかどうかを検出する必要があります。
最新の文字の Unicode 値をチェックするだけで十分だと思いますが、新しい絵文字 2 では、一部の文字が Unicode インデックス全体に散らばっています (つまり、Apple の新しく設計された著作権と登録ロゴ)。
おそらく、NSLocale または LocalizedString 値で文字の言語をチェックすることと関係があるのでしょうか?
誰か良い解決策を知っていますか?
ありがとう!
何年にもわたって、これらの絵文字検出ソリューションは、Apple が新しい方法を使用して新しい絵文字を追加するなどして壊れ続けています (追加の文字で文字を事前に呪うことによって作成された肌色の絵文字など)。
私はついに故障し、現在のすべての絵文字で機能し、将来のすべての絵文字で機能する次のメソッドを書きました。
このソリューションは、文字と黒の背景を持つ UILabel を作成します。次に、CG はラベルのスナップショットを撮り、スナップショット内のすべてのピクセルをスキャンして、黒以外のピクセルを探します。黒い背景を追加する理由は、サブピクセル レンダリングによる偽色の問題を回避するためです。
ソリューションは私のデバイスで非常に高速に実行され、1 秒間に数百文字をチェックできますが、これは CoreGraphics ソリューションであり、通常のテキスト メソッドのように頻繁に使用しないでください。グラフィック処理はデータ量が多いため、一度に数千の文字をチェックすると、顕著な遅延が発生する可能性があります。
-(BOOL)isEmoji:(NSString *)character {
UILabel *characterRender = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
characterRender.text = character;
characterRender.backgroundColor = [UIColor blackColor];//needed to remove subpixel rendering colors
[characterRender sizeToFit];
CGRect rect = [characterRender bounds];
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
CGContextRef contextSnap = UIGraphicsGetCurrentContext();
[characterRender.layer renderInContext:contextSnap];
UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRef imageRef = [capturedImage CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);
BOOL colorPixelFound = NO;
int x = 0;
int y = 0;
while (y < height && !colorPixelFound) {
while (x < width && !colorPixelFound) {
NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
CGFloat red = (CGFloat)rawData[byteIndex];
CGFloat green = (CGFloat)rawData[byteIndex+1];
CGFloat blue = (CGFloat)rawData[byteIndex+2];
CGFloat h, s, b, a;
UIColor *c = [UIColor colorWithRed:red green:green blue:blue alpha:1.0f];
[c getHue:&h saturation:&s brightness:&b alpha:&a];
b /= 255.0f;
if (b > 0) {
colorPixelFound = YES;
}
x++;
}
x=0;
y++;
}
return colorPixelFound;
}
まず、あなたの「55357 メソッド」と、それが多くの絵文字で機能する理由について説明しましょう。
Cocoa では、 anNSString
は のコレクションでありunichar
、がと同じunichar
typealias にすぎません。の最大値は であるため、絵文字に使用される 6 つの主要な Unicode ブロックのうち 2 つだけがこの範囲に該当するため、かなりの数の絵文字が 1 つの に収まることができなくなります。unsigned short
UInt16
UInt16
0xffff
unichar
これらのブロックには 113 個の絵文字が含まれており、1 つのブロックとして表すことができる追加の 66 個の絵文字が、unichar
他のさまざまなブロックに散らばっています。ただし、これらの 179 文字は1126 個の絵文字基本文字の一部にすぎず、残りは複数の で表す必要がありますunichar
。
コードを分析しましょう。
unichar unicodevalue = [text characterAtIndex:0];
何が起こっているかというと、単純に文字列の最初unichar
の文字列を取得しているということです。これは、前述の 179 文字では機能しますが、UTF-32 文字に遭遇すると、NSString
すべてが UTF-16 エンコーディングに変換されるため、バラバラになります。変換は、UTF-32 値をサロゲート ペアに置き換えることで機能します。つまり、NSString
に 2 つの が含まれるようになりましたunichar
。
これで、55357 という数字0xd83d
が多くの絵文字に表示される理由がわかりました。UTF-32 文字の最初の UTF-16 値だけを見ると、それぞれが 1024 のスパンを持つ高いサロゲートが得られます。低サロゲート。上位サロゲートの範囲0xd83d
は U+1F400–U+1F7FF で、これは最大の絵文字ブロックであるその他の記号と絵文字(U+1F300–U+1F5FF) の真ん中から始まり、拡張された幾何学図形までずっと続きます。(U+1F780–U+1F7FF) – 合計 563 個の絵文字と、この範囲内の 333 個の非絵文字文字を含みます。
つまり、絵文字ベース文字の印象的な 50% が高いサロゲートを持っています0xd83d
が、これらの推定方法ではまだ 384 個の絵文字文字が未処理のままであり、少なくとも同じ数の絵文字が誤検知されています。
私は最近、Swift の実装に関するやや関連する質問に回答しました。必要に応じて、このフレームワークで絵文字がどのように検出されるかを確認できます。これは、標準の絵文字をカスタム画像に置き換える目的で作成したものです。
とにかく、できることは文字から UTF-32 コード ポイントを抽出することです。これは、仕様に従って行います。
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
// Get the UTF-16 representation of the text.
unsigned long length = text.length;
unichar buffer[length];
[text getCharacters:buffer];
// Initialize array to hold our UTF-32 values.
NSMutableArray *array = [[NSMutableArray alloc] init];
// Temporary stores for the UTF-32 and UTF-16 values.
UTF32Char utf32 = 0;
UTF16Char h16 = 0, l16 = 0;
for (int i = 0; i < length; i++) {
unichar surrogate = buffer[i];
// High surrogate.
if (0xd800 <= surrogate && surrogate <= 0xd83f) {
h16 = surrogate;
continue;
}
// Low surrogate.
else if (0xdc00 <= surrogate && surrogate <= 0xdfff) {
l16 = surrogate;
// Convert surrogate pair to UTF-32 encoding.
utf32 = ((h16 - 0xd800) << 10) + (l16 - 0xdc00) + 0x10000;
}
// Normal UTF-16.
else {
utf32 = surrogate;
}
// Add UTF-32 value to array.
[array addObject:[NSNumber numberWithUnsignedInteger:utf32]];
}
NSLog(@"%@ contains values:", text);
for (int i = 0; i < array.count; i++) {
UTF32Char character = (UTF32Char)[[array objectAtIndex:i] unsignedIntegerValue];
NSLog(@"\t- U+%x", character);
}
return YES;
}
「」と入力するUITextView
と、コンソールに次のように書き込まれます。
contains values:
- U+1f60e
そのロジックを使用して、の値をcharacter
絵文字コード ポイントのデータ ソースと比較するだけで、その文字が絵文字かどうかを正確に知ることができます。
PS
Variation Selectorsとzero-width joinersなどの「見えない」文字もいくつかありますが、これらも処理する必要があるため、それらの動作を学習するために学習することをお勧めします。
別の解決策: https://github.com/woxtu/NSString-RemoveEmoji
次に、この拡張機能をインポートした後、次のように使用できます。
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// Detect if an Emoji is in the string "text"
if(text.isIncludingEmoji) {
// Show an UIAlertView, or whatever you want here
return NO;
}
return YES;
}
それが役立つことを願っています;)
Swiftでの絵文字検出方法は次のとおりです。それは正常に動作します。それが他の人を助けることを願っています。
func isEmoji(_ character: String?) -> Bool {
if character == "" || character == "\n" {
return false
}
let characterRender = UILabel(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
characterRender.text = character
characterRender.backgroundColor = UIColor.black
characterRender.sizeToFit()
let rect: CGRect = characterRender.bounds
UIGraphicsBeginImageContextWithOptions(rect.size, true, 0.0)
if let contextSnap:CGContext = UIGraphicsGetCurrentContext() {
characterRender.layer.render(in: contextSnap)
}
let capturedImage: UIImage? = (UIGraphicsGetImageFromCurrentImageContext())
UIGraphicsEndImageContext()
var colorPixelFound:Bool = false
let imageRef = capturedImage?.cgImage
let width:Int = imageRef!.width
let height:Int = imageRef!.height
let colorSpace = CGColorSpaceCreateDeviceRGB()
let rawData = calloc(width * height * 4, MemoryLayout<CUnsignedChar>.stride).assumingMemoryBound(to: CUnsignedChar.self)
let bytesPerPixel:Int = 4
let bytesPerRow:Int = bytesPerPixel * width
let bitsPerComponent:Int = 8
let context = CGContext(data: rawData, width: Int(width), height: Int(height), bitsPerComponent: Int(bitsPerComponent), bytesPerRow: Int(bytesPerRow), space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue)
context?.draw(imageRef!, in: CGRect(x: 0, y: 0, width: width, height: height))
var x:Int = 0
var y:Int = 0
while (y < height && !colorPixelFound) {
while (x < width && !colorPixelFound) {
let byteIndex: UInt = UInt((bytesPerRow * y) + x * bytesPerPixel)
let red = CGFloat(rawData[Int(byteIndex)])
let green = CGFloat(rawData[Int(byteIndex+1)])
let blue = CGFloat(rawData[Int(byteIndex + 2)])
var h: CGFloat = 0.0
var s: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 0.0
var c = UIColor(red:red, green:green, blue:blue, alpha:1.0)
c.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
b = b/255.0
if Double(b) > 0.0 {
colorPixelFound = true
}
x+=1
}
x=0
y+=1
}
return colorPixelFound
}
これを使用して、ASCII文字のみが含まれているかどうかを検出できます。
[myString canBeConvertedToEncoding:NSASCIIStringEncoding];
失敗した場合(または絵文字がある場合)はノーと言います。次に、Enter キーなどのクリックを許可しない if else ステートメントを実行できます。
絵文字の長さは 2 なので、キーボード ヒットの各キーの後に呼び出される shouldChangeTextInRange: メソッドで文字列の長さが 2 かどうかを確認します。
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// Detect if an Emoji is in the string "text"
if([text length]==2) {
// Show an UIAlertView, or whatever you want here
return YES;
}
else
{
return NO;
}
}