UILabel мәтіні түрлендірілгенін қалай тексеруге болады?

Мен UILabel түртілгенін тексергім келеді. Бірақ мен одан да көп нәрсе қажет. Мәтін тоқталды ма? Мен қазір UILabel фреймі осыған сүйене отырып, тек шын/жалғанды ​​аламын:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
    if (CGRectContainsPoint([self.currentLetter frame], [touch locationInView:self.view]))
    {
        NSLog(@"HIT!");
    }
}

Мұны тексерудің қандай жолы бар ма? UILabel ішіндегі хаттың сыртына бірден тиіп кетсем, мен жалған қайтаруды қалаймын.

Қара қара түсті «мәтіндік пиксельдер» қашан басылғанын білгім келеді.

Рахмет!

12
Сіздердің хаттарыңыз адамның саусақтары үшін өте жұқа болар деп ойлайсыз ба?
қосылды автор Khanh Nguyen, көзі
Сіз мұны c ++-де жасай аласыз және суретті тану үшін пайдаланатын кодты бере аламын, бірақ EasyBMP-мен айтқанымдай C ++ сияқты қолдандым ... Сіз тек фотосүрісте суретті/мәтінді жаңадан құрылған фотошоп кескіні ретінде фондық суретті жасау үшін, uilabel-ді қолданыңыз және импорттаңыз. Содан кейін кескінді өңдейтін және пикселдерді жазатын, оларды массив нысанында сақтайтын және қимыл танғышты қосқан кезде осы пикселдерге сілтейтін әдістерді іске асыратын сыныпты жасаңыз ... Менің ойымша, өте дамыған, бірақ мүмкін.
қосылды автор jsetting32, көзі
Нақты мәселе - нақты кейіпкерлердің орнына пикселдерді тану. С ++ жобасымен өткен семестрде не істеуім керек, бұл сурет тану болды. Мүмкін, iOS кітапханасы мұндай әрекеттерді жасауы керек, бірақ сіз EVERY pixel арқылы шын мәнінде іздейтін кірістірілген цикл жасауды жоспарлап отырсыз ... өң түсінің сипатын таңба түстерімен салыстырыңыз және түс өзгерісі бар болса, бұл бағдарламаның өмір бойы сақталатын немесе жүктелген көріністің өмірлік циклі бойында сақталатын нысанның немесе аргументтің ішінде «ҰЛЫ ОРАЛУ» ішінде пиксель мәнін сақтау ...
қосылды автор jsetting32, көзі
@JoakimSerholt бұл өте әдемі, себебі оған кейбір ұқыпты практикалық қосымшалар болуы мүмкін. Мен жай ғана білмеймін. Айтыңызшы, бұл сіз не үшін пайдаландыңыз? Мүмкін, осы тұжырымдаманы қолдануға болатын басқа мысалдар бар ма? Адамға рахмет!
қосылды автор Pavan, көзі
currentLetter дегеніміз не? «Хаттың сыртында» деген не?
қосылды автор Wain, көзі
Нгуенмен келісемін, бұл ақылсыз идея, бірақ мұнда сіз қалай жасайсыз. Белгінің түсірілімін алыңыз және түстің мәнін/rgb деректерін алыңыз. нүкте қозғалды.
қосылды автор Christian Schnorr, көзі
Мәтіннің үлкенірек немесе нақты пикселдер қара түспен көрсетілуіне байланысты орынды білдіресіз бе?
қосылды автор Christian Schnorr, көзі
Адам саусақтары үшін кішкентай? Бұл кейіпкерлердің қаншалықты үлкен емес екеніне байланысты. Мен жартысы ipad экраны үшін жеткілікті үлкен кейіпкерлермен жұмыс істеймін. Осы тақырыптағы барлық жауаптарыңыз үшін рақмет, бұл адамдар тез арада нашар жұмыс жасап, нәтижелерімен қайтарылады.
қосылды автор Joakim Serholt, көзі
@ ices_2 UILabel-дан UIButton-ге ауысуға ешқандай қиындықтарым жоқ, бірақ маған көмектесе беріңіз, нақты «мәтіндік-пикселдер» басылғанын қалай білемін?
қосылды автор Joakim Serholt, көзі
@Jenox Пайдаланушы қара көрсетілетін пикселдерге тиіп тұрғанын тексеру керек.
қосылды автор Joakim Serholt, көзі
UILabel-ді қажет болса кез келген басқа затпен алмастыра аламын, тіпті суреттер де жақсы бола алар ма еді, PNG-нің мөлдір фонды жұмыс істеуі қандай еді?
қосылды автор Joakim Serholt, көзі
фишкаларсыз uibuto жасау керек пе?
қосылды автор p.balmasov, көзі

8 жауаптар

tl;dr: You can hit test the path of the text. Gist is available here.


Мен жүретін жол көздің нүктесінің мәтіннің жолында тұрғанын немесе жоқ екенін тексеру. Тегжейлі түсіндірмес бұрын сізге қадамдар туралы қысқаша шолу беріңіз.

  1. UILabel сыныбы
  2. Мәтіннің CGPath мәтінін алу үшін негізгі мәтінді пайдаланыңыз
  3. Нүктені ішіндегі немесе ішіне қарастыру керек пе, анықтау үшін pointInside: withEvent: дегенді ауыстырыңыз.
  4. Кез келген «қалыпты» сенсорлық өңдеуді, мысалы, шабуыл жасалған кезде білу үшін, қимыл туралы ақпаратты тану құралын пайдаланыңыз.

Бұл тәсілдің үлкен артықшылығы мынада, қаріпті дәл түсіндіреді және төменде көрсетілгендей «хитеттелген» аймақты өсіру жолын өзгерте аласыз. Қара және қызғылт сары бөліктердің екеуі де таспалы болып табылады, бірақ жапсырмада қара бөлшектер ғана шығарылады.

tap area

UILabel сыныбы

<�Код> UILabel деп аталатын TextHitTestingLabel деп аталатын қосалқы сыныбы жасалды және мәтін жолы үшін жеке сипатты қосылды.

@interface TextHitTestingLabel (/*Private stuff*/)
@property (assign) CGPathRef textPath;
@end

IOS жапсырмаларында text немесе atttedText болуы мүмкін болғандықтан, мен осы екі әдісті де қосып, оларды мәтін жолын жаңарту әдісін шақырдым.

- (void)setText:(NSString *)text {
    [super setText:text];

    [self textChanged];
}

- (void)setAttributedText:(NSAttributedString *)attributedText {
    [super setAttributedText:attributedText];

    [self textChanged];
}

Сондай-ақ, NIB/Storyboard-дан жапсырма жасалуы мүмкін, бұл жағдайда мәтін бірден орнатылады. Бұл жағдайда мен бастапқы мәтінді NIB-дан оятуға тексеремін.

- (void)awakeFromNib {
    [self textChanged];
}

Мәтін жолын алу үшін негізгі мәтінді пайдаланыңыз

Негізгі мәтін - мәтінді көрсетуді толық бақылауға мүмкіндік беретін төмен деңгейлі құрылым. Жобаңызға CoreText.framework қосуыңыз керек және оны файлыңызға импорттаңыз

#import 

textChanged ішіндегі бірінші нәрсе мәтінді алу болып табылады. iOS 6 немесе одан бұрынғы нұсқасына байланысты, мен берілген мәтінді тексеруім керек. Белгідегілердің біреуі ғана болады.

// Get the text
NSAttributedString *attributedString = nil;
if ([self respondsToSelector:@selector(attributedText)]) {//Available in iOS 6
    attributedString = self.attributedText; 
}
if (!attributedString) {//Either earlier than iOS6 or the `text` property was set instead of `attributedText`
    attributedString = [[NSAttributedString alloc] initWithString:self.text
                                                       attributes:@{NSFontAttributeName: self.font}];
}

Содан кейін барлық хат глифтеріне арналған жаңа өзгермелі жолды жасаймын.

// Create a mutable path for the paths of all the letters.
CGMutablePathRef letters = CGPathCreateMutable();

Негізгі мәтін «магия»

Негізгі мәтін мәтіннің және глифтің және глифтің сызықтарымен жұмыс істейді. Мысалы, егерде мәтін бар болса: « Hel lo« (анықтығы үшін қосылатын кеңістік) сияқты атрибуттары бар »Сәлем«. Содан кейін бұл екі глифпен жұмыс істейтін мәтіннің бір сызығы болады: біреуі батыл және біреуі. Алғашқы глифтік жүгіруде 3 глиф бар, ал екінші жүгіруде 2 глиф бар.

Мен барлық глифті жүгірістер мен олардың глифтерін аударып, CTFontCreatePathForGlyph() жолын алыңыз. Содан кейін әр жеке глиф жолы өзгертілетін жолға қосылады.

// Create a line from the attributed string and get glyph runs from that line
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attributedString);
CFArrayRef runArray = CTLineGetGlyphRuns(line);

// A line with more then one font, style, size etc will have multiple fonts.
// "Hello" formatted as " *Hel* lo " (spaces added for clarity) is two glyph
// runs: one italics and one regular. The first run contains 3 glyphs and the
// second run contains 2 glyphs.
// Note that " He *ll* o " is 3 runs even though "He" and "o" have the same font.
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
   //Get the font for this glyph run.
    CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
    CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);

   //This glyph run contains one or more glyphs (letters etc.)
    for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
    {
       //Read the glyph itself and it position from the glyph run.
        CFRange glyphRange = CFRangeMake(runGlyphIndex, 1);
        CGGlyph glyph;
        CGPoint position;
        CTRunGetGlyphs(run, glyphRange, &glyph);
        CTRunGetPositions(run, glyphRange, &position);

       //Create a CGPath for the outline of the glyph
        CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
       //Translate it to its position.
        CGAffineTransform t = CGAffineTransformMakeTranslation(position.x, position.y);
       //Add the glyph to the 
        CGPathAddPath(letters, &t, letter);
        CGPathRelease(letter);
    }
}
CFRelease(line);

Негізгі мәтіндік координат жүйесі UIView жүйелі жүйесімен салыстырмалы түрде төмен, сондықтан мен экранда көргенімізге сәйкес келетін жолды аударамын.

// Transform the path to not be upside down
CGAffineTransform t = CGAffineTransformMakeScale(1, -1);//flip 1
CGSize pathSize = CGPathGetBoundingBox(letters).size; 
t = CGAffineTransformTranslate(t, 0, -pathSize.height);//move down

// Create the final path by applying the transform
CGPathRef finalPath = CGPathCreateMutableCopyByTransformingPath(letters, &t);

// Clean up all the unused path
CGPathRelease(letters);

self.textPath = finalPath;

Енді менде жапсырма мәтіні үшін толық CGPath бар.

Override pointInside:withEvent:

Енгізу нүктесін ішіндегі қандай нүктелер деп қарастыра аламын, ішіндегі нүктені алдын ала тексеріп, нүктенің мәтін жолында екенін тексеремін. UIKit-тің басқа бөліктері осы әдісті хит-тестілеуге шақырады.

// Override -pointInside:withEvent to determine that ourselves.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
   //Check if the points is inside the text path.
    return CGPathContainsPoint(self.textPath, NULL, point, NO);
}

Қалыпты сенсорлық өңдеу

Now everything is setup to work with Қалыпты сенсорлық өңдеу. I added a tap recognizer to my label in a NIB and connected it to a method in my view controller.

- (IBAction)labelWasTouched:(UITapGestureRecognizer *)sender {
    NSLog(@"LABEL!");
}

Мұның барлығы қажет. Егер сіз мұнда төмен қарай жылжып, әртүрлі код бөлімдерін алып, оларды біріктіргіңіз келмесе, бүкіл .m файлын жүктеп, қолдануға болатын <.

Ескертпе, көптеген қаріптер сенсорлық дәлдікпен (44 пиксель) салыстырғанда өте жұқа, ал сенсорлар «босатылған» деп саналған кезде өте ренжітуі мүмкін. Мұны айтады: бақытты кодтау!


Жаңарту:

Пайдаланушыға сәл жағымды болу үшін, хит тестілеуі үшін қолданылатын мәтін жолын инсульт аласыз. Бұл сенсорлыққа түсетін үлкен аймақты береді, бірақ мәтінді түртіп жатқаныңыз туралы сезім береді.

CGPathRef endPath = CGPathCreateMutableCopyByTransformingPath(letters, &t);

CGMutablePathRef finalPath = CGPathCreateMutableCopy(endPath);
CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(endPath, NULL, 7, kCGLineCapRound, kCGLineJoinRound, 0);
CGPathAddPath(finalPath, NULL, strokedPath);

// Clean up all the unused paths
CGPathRelease(strokedPath);
CGPathRelease(letters);
CGPathRelease(endPath);

self.textPath = finalPath;

Now the orange area in the image below is going to be tappable as well. This still feels like you are touching the text but is less annoying to the users of your app. tap area

Егер сіз бұл мәтінді одан да оңай соғуды жеңілдету үшін оны одан әрі алайық деп ойласаңыз, бірақ ол кез келген сәтте барлық жапсырмаға сәйкес келуі мүмкін.

Huge tap area

19
қосылды
Бұл үлкен көзқарас.
қосылды автор sudo rm -rf, көзі
Мен көріп отырған компиляция қатесі CTLineCreateWithAttributedString-ге жіберілген параметр үшін жабық көпірдің берілуіне байланысты. Шеберге __bridge қосып оны жұмыс істейді.
қосылды автор TomSwift, көзі
Өзіңізді көрсететін мәтін бойынша жалпы хит-тестілеудің ең жақсы әдістемесі деп ойлаймын. Бірақ UILabel-мен қолданған кезде, кейбір мәселелер бар: 1) мен бұл мәтінді өте тығыз ұстау үшін өзімнің жапсырмасының шектік қорабын қайта өлшегенше жұмыс істей алмадым. Егер жапсырма рамкасы ұзын болса (немесе әдепкі sizeToFit биіктігі) болса, хит-тестілеу y-осінде көрсетілетінге сәйкес келмеді. 2) UILabel қолдануы мүмкін кез-келген автоқалпына жауап бермейді. 3) Autoshrink қолдауы туралы сенімсіздік - екіталай. 4) Xcode 4.6-де жасалмайды (белгілі бір Әзірлеушіні алдын ала қарауда жинайды ...)
қосылды автор TomSwift, көзі
@TomSwift - бұл өте жақсы кері байланыс. Рақмет сізге. Мен сол нәрселерді шешу үшін не істей алатынымды көремін.
қосылды автор David Rönnqvist, көзі
+1 шынымен, үлкен жауап.
қосылды автор SAMIR RATHOD, көзі
Үлкен жауап, Сізге арқасында жаңа материалдарды үйрендім;)
қосылды автор BaNz, көзі

Мәселе, менің түсінуімше, UILabel мәтінін құрайтын глифтердің бірінде кран (сенсор) болғанда. Егер сенсор глифтердің кез келген жолының сыртында болса, ол саналмайды.

Міне, менің шешімім. Белгісі бар көрініске байланысты UILabel * ivar және UITapGestureRecognizer дегенді білдіреді.

- (IBAction) onTouch: (UITapGestureRecognizer*) tgr
{
    CGPoint p = [tgr locationInView: _label];

   //in case the background of the label isn't transparent...
    UIColor* labelBackgroundColor = _label.backgroundColor;
    _label.backgroundColor = [UIColor clearColor];

   //get a UIImage of the label
    UIGraphicsBeginImageContext( _label.bounds.size );
    CGContextRef c = UIGraphicsGetCurrentContext();
    [_label.layer renderInContext: c];
    UIImage* i = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

   //restore the label's background...
    _label.backgroundColor = labelBackgroundColor;

   //draw the pixel we're interested in into a 1x1 bitmap
    unsigned char pixel = 0x00;
    c = CGBitmapContextCreate(&pixel,
                              1, 1, 8, 1, NULL,
                              kCGImageAlphaOnly);
    UIGraphicsPushContext(c);
    [i drawAtPoint: CGPointMake(-p.x, -p.y)];
    UIGraphicsPopContext();
    CGContextRelease(c);

    if ( pixel != 0 )
    {
        NSLog( @"touched text" );
    }
}
7
қосылды
Бұл кодтың тамаша үзіндісі, Том! Пайдаланушы глифтерді таңдай ма немесе жоқ па екенін анықтау үшін белгілі бір пиксельдің альфа арнасын анықтау тұжырымдамасы. Бұл үшін практикалық қосымшалар туралы білесіз бе?
қосылды автор Pavan, көзі
Бұл жақсы жұмыс істеді және іске асыруға маған 5 минуттай түсті. Мен сенімдімін, басқа да жақсы ұсыныс бар балама нұсқада, unfortinatly im емес, онда мен олардың барлық тестілеу мүмкін. @TomSwift деген жауап беріп жатырмын, себебі сіздің кодыңыз менің қажеттіліктеріме сәйкес келеді.
қосылды автор Joakim Serholt, көзі

You can use a UIGestureRecognizer: http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html

Атап айтқанда, UITapGestureRecognizer қолданғыңыз келеді деп ойлаймын. Егер сіз мәтіндік жақтау түртілгенін білгіңіз келсе, сіздің кодтың өлшемін [yourLabel sizeToFit] -мен сәйкестендіру үшін оңайлатылады.

Қалай болса да, мұны істеу үшін UIButton пайдалану үшін барамын, бұл оңай нұсқасы.

Егер сіз тек нақты мәтінді ғана емес, барлық UITextField фреймін таппаған жағдайда ғана анықтау қажет болса, ол әлдеқайда қиын болады. Бір тәсіл қолданушы пикселдің қараңғылығын анықтайды, бірақ бұл кейбір қатал код . Қалай болса да, сіздің өтініміңіздегі күткен өзара әрекеттесуге байланысты жұмыс істей алады. Бұл SO сұрағын тексеріңіз:

iOS - пиксельдің түсі анықталады?

Барлық көрсетілген пиксельдің 100% қара емес екенін ескеру керек еді, сондықтан жақсы нәтижелерге жету үшін шектімен ойнайтын болар едім.

4
қосылды
Сіз мұны өзі айттыңыз, кадрдың өлшемін орнатыңыз. Пайдаланушы жақтауды қай кезде меңгергенін білгім келмейді, пайдаланушы нақты мәтінді қашан басқанын білгім келеді, хат жасаған қара түсті пикселдер.
қосылды автор Joakim Serholt, көзі
@ JoakimBörjesson Жауап жаңартылды ;-) Оңтайлы шешім емес (өте алыс), бірақ оны көріңіз :-)
қосылды автор atxe, көзі

Менің ойымша, ол жапсырмадағы әріптің басқа бөліктерге емес, басқа әріптерге тиетінін білгісі келеді. Осы мақсатқа жету үшін мөлдір кескінді қолдануға дайын болғандықтан, мысалы, сізде «А» әрпі ашық фоны бар, егер монотонды болса, хаттың түсі болса, бұл жағдайда қызыл деп айтуға болады UIImage-дің CGImage-ді, провайдерді алып, оны нүктелік кескін ретінде көрсетіңіз және нүктенің түсі қызыл түске боялады ма. Басқа түстер үшін сіз бұл суретті онлайн кескін өңдеушісі арқылы ғана таңдап, RGB мәнін алып, оған қарсы бола аласыз.

2
қосылды
Сізге әсер ететін нүктенің түсін қалай тексере алатыным туралы мысал бар ма? UILabel мәтінінің мәтінінің түсіне қарсы екенін дәлелдей алмаймын ба? Пожалуйста, сәл ойластырыңыз.
қосылды автор Joakim Serholt, көзі

Ең алдымен, қимыл туралы тану құралын жасап, оған қосылыңыз:

UITapGestureRecognizer * tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
[self.label addGestureRecognizer:tapRecognizer];
self.label.userInteractionEnabled = YES;

Now implement -tapGesture:

- (void)tapGesture:(UITapGestureRecognizer *)recognizer
{
   //Determine point touched
    CGPoint point = [recognizer locationInView:self.label];

   //Render UILabel in new context
    UIGraphicsBeginImageContext(self.label.bounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [self.label.layer renderInContext:context];

   //Getting RGBA of concrete pixel
    int bpr = CGBitmapContextGetBytesPerRow(context);
    unsigned char * data = CGBitmapContextGetData(context);
    if (data != NULL)
    {
        int offset = bpr*round(point.y) + 4*round(point.x);
        int red = data[offset+0];
        int green = data[offset+1];
        int blue = data[offset+2];
        int alpha =  data[offset+3];

        NSLog(@"%d %d %d %d", alpha, red, green, blue);

        if (alpha == 0)
        {
           //Here is tap out of text
        }
        else
        {
           //Here is tap right into text
        }
    }

    UIGraphicsEndImageContext();
}

Бұл UILabel-де мөлдір фонда жұмыс істейді, егер бұл сіз қаласаңыз, альфа, қызыл, жасыл, көкпен self.label.backgroundColor ...

0
қосылды

КөріністіDidLoad немесе IB арқылы Label жасаңыз және селектор арқылы кодты төменде пайдалану арқылы Қосу түймешігін басыңыз, содан кейін жапсырма журналы басылғанда (ол синглотапта :)

- (void)viewDidLoad
{
[super viewDidLoad];    
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(30, 0, 150, 35)];
label.userInteractionEnabled = YES;
label.backgroundColor = [UIColor greenColor];
label.text = @"label";
label.textAlignment = NSTextAlignmentCenter;

UITapGestureRecognizer * single = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singletap:)];
[label addGestureRecognizer:single];
single.numberOfTapsRequired = 1;
[self.view addSubview:label];


}
-(void) singletap:(id)sender
{
NSLog(@"single tap");
//do your stuff here
}

Егер табылған болса, оны оң деп белгілеңіз бақытты кодтау

0
қосылды

UILabel данасын қадағалауды қаласаңыз, userInteractionEnabled болып табылады.

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
    UIView *touchView = touch.view;
    if([touchView isKindOfClass:[UILabel class]]){
        NSLog(@"Touch event occured in Label %@",touchView);
    }
}
0
қосылды

Таңбаның орнына UIButton қолдануға болады:

- (void)viewDidLoad {
    [super viewDidLoad];
   //Do any additional setup after loading the view, typically from a nib.

    UIButton *tmpButton = [[UIButton alloc] initWithFrame:CGRectMake(50, 50, 100, 20)];
    [tmpButton setTitle:@"KABOYA" forState:UIControlStateNormal];
    [tmpButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [tmpButton addTarget:self
              action:@selector(buttonPressed:)
    forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:tmpButton];
}

Батырманы басқан кезде мұнда бірдеңені орындаңыз:

-(void)buttonPressed:(UIButton *)sender {
    NSLog(@"Pressed !");
}

Бұл көмектесті деп үміттенемін;)

0
қосылды
Егер мен А әріпін жазсам. Әрбектің айналасында қалыптасқан рамка (квадрат) ішіне тиіп тұрғанда, uibutton сенсоры іске қосылады. Қажетті қара пикселдер қозғалғанда ғана оқиға жасау керек. Егер сіз O ішіне бассаңыз, онда ол да іске қосылмауы керек.
қосылды автор Joakim Serholt, көзі