Jak sprawić, by UILabel wyświetlał zarysowany tekst?

Wszystko, czego chcę, to czarna ramka o jednym pikselu wokół mojego białego tekstu UILabel.

Doszedłem do podklasowania UILabel poniższym kodem, który niezdarnie połączyłem z kilkoma pokrewnymi przykładami online. I działa, ale jest bardzo, bardzo wolno (poza symulatorem) i nie mogłem go wyśrodkować pionowo tekstu (więc tymczasowo zakodowałem wartość y w ostatniej linii). Ahhhh!

void ShowStringCentered(CGContextRef gc, float x, float y, const char *str) {
    CGContextSetTextDrawingMode(gc, kCGTextInvisible);
    CGContextShowTextAtPoint(gc, 0, 0, str, strlen(str));
    CGPoint pt = CGContextGetTextPosition(gc);

    CGContextSetTextDrawingMode(gc, kCGTextFillStroke);

    CGContextShowTextAtPoint(gc, x - pt.x / 2, y, str, strlen(str));
}


- (void)drawRect:(CGRect)rect{

    CGContextRef theContext = UIGraphicsGetCurrentContext();
    CGRect viewBounds = self.bounds;

    CGContextTranslateCTM(theContext, 0, viewBounds.size.height);
    CGContextScaleCTM(theContext, 1, -1);

    CGContextSelectFont (theContext, "Helvetica", viewBounds.size.height,  kCGEncodingMacRoman);

    CGContextSetRGBFillColor (theContext, 1, 1, 1, 1);
    CGContextSetRGBStrokeColor (theContext, 0, 0, 0, 1);
    CGContextSetLineWidth(theContext, 1.0);

    ShowStringCentered(theContext, rect.size.width / 2.0, 12, [[self text] cStringUsingEncoding:NSASCIIStringEncoding]);
}
Mam tylko dokuczliwe uczucie, że pomijam prostsze dobra robota. Być może poprzez nadpisanie "drawTextInRect", ale nie mogę sprawić, by drawTextInRect w ogóle się pochylił, mimo że wpatrywał się w niego uważnie i marszczył brwi naprawdę bardzo mocno.
Author: Axel Guilmin, 2009-07-09

12 answers

Udało mi się to zrobić przez nadpisanie drawTextInRect:

- (void)drawTextInRect:(CGRect)rect {

  CGSize shadowOffset = self.shadowOffset;
  UIColor *textColor = self.textColor;

  CGContextRef c = UIGraphicsGetCurrentContext();
  CGContextSetLineWidth(c, 1);
  CGContextSetLineJoin(c, kCGLineJoinRound);

  CGContextSetTextDrawingMode(c, kCGTextStroke);
  self.textColor = [UIColor whiteColor];
  [super drawTextInRect:rect];

  CGContextSetTextDrawingMode(c, kCGTextFill);
  self.textColor = textColor;
  self.shadowOffset = CGSizeMake(0, 0);
  [super drawTextInRect:rect];

  self.shadowOffset = shadowOffset;

}
 158
Author: kprevas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-05 06:45:42

Prostszym rozwiązaniem jest użycie przypisanego ciągu w ten sposób:

Swift 4:

let strokeTextAttributes: [NSAttributedStringKey : Any] = [
    NSAttributedStringKey.strokeColor : UIColor.black,
    NSAttributedStringKey.foregroundColor : UIColor.white,
    NSAttributedStringKey.strokeWidth : -2.0,
    ]

myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)

Swift 4.2:

let strokeTextAttributes: [NSAttributedString.Key : Any] = [
    .strokeColor : UIColor.black,
    .foregroundColor : UIColor.white,
    .strokeWidth : -2.0,
    ]

myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)

Na UITextField można również ustawić defaultTextAttributes i attributedPlaceholder.

Zauważ, że NSStrokeWidthAttributeName musi być ujemne w tym przypadku, tzn. działają tylko wewnętrzne kontury.

UITextFields z konturem za pomocą przypisanych tekstów

 90
Author: Axel Guilmin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-11 08:29:00

Po przeczytaniu zaakceptowanej odpowiedzi i dwóch poprawek do niej oraz odpowiedzi od Axela Guilmina, postanowiłem skompilować ogólne rozwiązanie w języku Swift, które mi odpowiada:

import UIKit

class UIOutlinedLabel: UILabel {

    var outlineWidth: CGFloat = 1
    var outlineColor: UIColor = UIColor.whiteColor()

    override func drawTextInRect(rect: CGRect) {

        let strokeTextAttributes = [
            NSStrokeColorAttributeName : outlineColor,
            NSStrokeWidthAttributeName : -1 * outlineWidth,
        ]

        self.attributedText = NSAttributedString(string: self.text ?? "", attributes: strokeTextAttributes)
        super.drawTextInRect(rect)
    }
}

Możesz dodać tę niestandardową klasę UILabel do istniejącej etykiety w Kreatorze interfejsów i zmienić grubość obramowania i jej kolor, dodając zdefiniowane przez użytkownika atrybuty środowiska pracy w następujący sposób: Konfiguracja konstruktora interfejsu

Wynik:

duże czerwone G z konturem

 20
Author: Lachezar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-08 18:30:06

Jest jeden problem z implementacją odpowiedzi. Rysowanie tekstu z obrysem ma nieco inną szerokość glifu znakowego niż rysowanie tekstu bez obrysu, co może powodować "niecentrowane" wyniki. Można to naprawić, dodając niewidoczny obrys wokół tekstu wypełnienia.

Zastąpić:

CGContextSetTextDrawingMode(c, kCGTextFill);
self.textColor = textColor;
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];

Z:

CGContextSetTextDrawingMode(context, kCGTextFillStroke);
self.textColor = textColor;
[[UIColor clearColor] setStroke]; // invisible stroke
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];

Nie jestem w 100% pewien, czy to prawda, bo nie wiem, czy self.textColor = textColor; ma taki sam efekt jak [textColor setFill], ale powinno zadziałać.

Disclosure: I ' m the twórca THLabel.

Opublikowałem jakiś czas temu podklasę UILabel, która umożliwia tworzenie konturów w tekście i innych efektach. Znajdziesz go tutaj: https://github.com/tobihagemann/THLabel

 7
Author: tobihagemann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 21:16:18

Nie spowoduje to utworzenia konturu per-se, ale spowoduje nałożenie cienia wokół tekstu, a jeśli promień cienia będzie wystarczająco mały, może on przypominać kontur.

label.layer.shadowColor = [[UIColor blackColor] CGColor];
label.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
label.layer.shadowOpacity = 1.0f;
label.layer.shadowRadius = 1.0f;
Nie wiem czy jest kompatybilny ze starszymi wersjami iOS.. Mam nadzieję, że to pomoże...
 6
Author: Suran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-23 21:26:26

Jeśli chcesz animować coś skomplikowanego, najlepszym sposobem jest zaprogramowanie zrzutu ekranu, który zamiast tego animować!

Aby zrobić zrzut ekranu widoku, potrzebujesz kodu trochę w ten sposób:

UIGraphicsBeginImageContext(mainContentView.bounds.size);
[mainContentView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext(); 

Gdzie mainContentView to widok, z którego chcesz zrobić zrzut ekranu. Dodaj viewImage do interfejsu UIImageView i Animuj go.

Mam nadzieję, że to przyspieszy Twoją animację!!

N

 4
Author: Nick Cartwright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-09 10:59:44

Jak wspomniałemMuskleumble , granica zaakceptowanej odpowiedzi jest nieco poza centrum. Udało mi się to poprawić, ustawiając szerokość obrysu na zero, zamiast zmieniać kolor na Wyczyść.

Czyli zastąpienie:

CGContextSetTextDrawingMode(c, kCGTextFill);
self.textColor = textColor;
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];

Z:

CGContextSetTextDrawingMode(c, kCGTextFillStroke);
self.textColor = textColor;
CGContextSetLineWidth(c, 0); // set stroke width to zero
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];
Po prostu skomentowałbym jego odpowiedź, ale najwyraźniej nie jestem wystarczająco "godny zaufania".
 3
Author: ufmike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:27

Jeśli wszystko, co chcesz, to jedno pikselowa czarna ramka wokół mojego białego tekstu UILabel,

Then myślę, że utrudniasz problem... Nie wiem po pamięci, której funkcji' draw rect / frameRect ' powinieneś użyć, ale łatwo będzie Ci ją znaleźć. ta metoda po prostu demonstruje strategię (pozwól superklasowi wykonać pracę!):

- (void)drawRect:(CGRect)rect
{
  [super drawRect:rect];
  [context frameRect:rect]; // research which rect drawing function to use...
}
 2
Author: kent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-09 11:39:36

Znalazłem problem z główną odpowiedzią. Pozycja tekstu nie musi być wyśrodkowana poprawnie do lokalizacji subpikseli, dzięki czemu obrys może być niedopasowany wokół tekstu. Naprawiłem go za pomocą następującego kodu, który używa CGContextSetShouldSubpixelQuantizeFonts(ctx, false):

- (void)drawTextInRect:(CGRect)rect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    [self.textOutlineColor setStroke];
    [self.textColor setFill];

    CGContextSetShouldSubpixelQuantizeFonts(ctx, false);

    CGContextSetLineWidth(ctx, self.textOutlineWidth);
    CGContextSetLineJoin(ctx, kCGLineJoinRound);

    CGContextSetTextDrawingMode(ctx, kCGTextStroke);
    [self.text drawInRect:rect withFont:self.font lineBreakMode:NSLineBreakByWordWrapping alignment:self.textAlignment];

    CGContextSetTextDrawingMode(ctx, kCGTextFill);
    [self.text drawInRect:rect withFont:self.font lineBreakMode:NSLineBreakByWordWrapping alignment:self.textAlignment];
}

Zakłada to, że zdefiniowałeś textOutlineColor i {[3] } jako właściwości.

 1
Author: Robotbugs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-06 02:55:51

A Swift 4 class version based off the answer by kprevas

import Foundation
import UIKit

public class OutlinedText: UILabel{
    internal var mOutlineColor:UIColor?
    internal var mOutlineWidth:CGFloat?

    @IBInspectable var outlineColor: UIColor{
        get { return mOutlineColor ?? UIColor.clear }
        set { mOutlineColor = newValue }
    }

    @IBInspectable var outlineWidth: CGFloat{
        get { return mOutlineWidth ?? 0 }
        set { mOutlineWidth = newValue }
    }    

    override public func drawText(in rect: CGRect) {
        let shadowOffset = self.shadowOffset
        let textColor = self.textColor

        let c = UIGraphicsGetCurrentContext()
        c?.setLineWidth(outlineWidth)
        c?.setLineJoin(.round)
        c?.setTextDrawingMode(.stroke)
        self.textColor = mOutlineColor;
        super.drawText(in:rect)

        c?.setTextDrawingMode(.fill)
        self.textColor = textColor
        self.shadowOffset = CGSize(width: 0, height: 0)
        super.drawText(in:rect)

        self.shadowOffset = shadowOffset
    }
}

Może być zaimplementowany w całości w Konstruktorze interfejsu, ustawiając niestandardową klasę UILabel na OutlinedText. Następnie będziesz mieć możliwość ustawienia szerokości i koloru konturu w panelu Właściwości.

Tutaj wpisz opis obrazka

 0
Author: Chris Stillwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-02 18:10:12

Dlaczego nie utworzysz w Photoshopie interfejsu użytkownika o rozmiarze 1PX, a następnie nie ustawisz interfejsu użytkownika z obrazem i nie umieścisz go za etykietą UILabel?

Kod:

UIView *myView;
UIImage *imageName = [UIImage imageNamed:@"1pxBorderImage.png"];
UIColor *tempColour = [[UIColor alloc] initWithPatternImage:imageName];
myView.backgroundColor = tempColour;
[tempColour release];

Uratuje ci podklasowanie obiektu i jest to dość proste do zrobienia.

Nie wspominając o tym, że jeśli chcesz zrobić animację, jest ona wbudowana w klasę UIView.

 -3
Author: Brock Woolf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-09 11:01:22

Aby umieścić obramowanie z zaokrąglonymi krawędziami wokół UILabel wykonuję następujące czynności:

labelName.layer.borderWidth = 1;
labelName.layer.borderColor = [[UIColor grayColor] CGColor];
labelName.layer.cornerRadius = 10;

(nie zapomnij dodać QuartzCore/QuartzCore.h)

 -3
Author: mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-25 14:26:15