webentwicklung-frage-antwort-db.com.de

Wie bringe ich UILabel dazu, umrissenen Text anzuzeigen?

Ich möchte nur einen schwarzen Rand von einem Pixel um meinen weißen UILabel-Text.

Ich habe es geschafft, UILabel mit dem folgenden Code zu unterordnen, den ich aus ein paar tangential verwandten Online-Beispielen ungeschickt zusammengeschustert habe. Und es funktioniert, aber es ist sehr, sehr langsam (außer im Simulator) und ich konnte es auch nicht dazu bringen, den Text vertikal zu zentrieren (also habe ich den y-Wert in der letzten Zeile vorübergehend hartcodiert). 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]);
}

Ich habe nur das nörgelnde Gefühl, einen einfacheren Weg zu übersehen. Vielleicht, indem ich "drawTextInRect" überschreibe, aber ich kann DrawTextInRect nicht dazu bringen, mich meinem Willen zu beugen, obwohl ich es genau anstarrte und die Stirn runzelte.

99
Monte Hurd

Ich konnte es tun, indem ich drawTextInRect überschrieb:

- (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;

}
163
kprevas

Eine einfachere Lösung ist die Verwendung eines Attributed String wie folgt:

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)

Auf einem UITextField können Sie auch den defaultTextAttributes und den attributedPlaceholder einstellen.

Beachten Sie, dass NSStrokeWidthAttributeNamemuss negativ sein in diesem Fall, d. H. Nur die inneren Umrisse funktionieren.

UITextFields with an outline using attributed texts

100
Axel Guilmin

Nachdem ich die akzeptierte Antwort und die beiden Korrekturen sowie die Antwort von Axel Guilmin gelesen hatte, entschied ich mich, eine Gesamtlösung in Swift zu erstellen, die zu mir passt:

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)
    }
}

Sie können diese benutzerdefinierte UILabel-Klasse zu einer vorhandenen Beschriftung im Interface Builder hinzufügen und die Dicke des Rahmens und seine Farbe ändern, indem Sie benutzerdefinierte Laufzeitattribute wie folgt hinzufügen: Interface Builder setup

Ergebnis:

big red G with outline

24
Lachezar

Es gibt ein Problem bei der Implementierung der Antwort. Das Zeichnen eines Textes mit Strich hat eine etwas andere Zeichenbreite als das Zeichnen eines Textes ohne Strich, was zu "nicht zentrierten" Ergebnissen führen kann. Dies kann durch Hinzufügen eines unsichtbaren Strichs um den Füllungstext behoben werden.

Ersetzen:

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

mit:

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

Ich bin mir nicht 100% sicher, ob das der wahre Deal ist, weil ich nicht weiß, ob self.textColor = textColor; hat den gleichen Effekt wie [textColor setFill], aber es sollte funktionieren.

Offenlegung: Ich bin der Entwickler von THLabel.

Ich habe vor einiger Zeit eine UILabel-Unterklasse veröffentlicht, die eine Gliederung in Text und andere Effekte ermöglicht. Sie finden es hier: https://github.com/tobihagemann/THLabel

8
tobihagemann

Dadurch wird an sich kein Umriss erstellt, aber der Text wird mit einem Schatten versehen. Wenn Sie den Schattenradius so klein machen, dass er einem Umriss ähnelt.

label.layer.shadowColor = [[UIColor blackColor] CGColor];
label.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
label.layer.shadowOpacity = 1.0f;
label.layer.shadowRadius = 1.0f;

Ich weiß nicht, ob es mit älteren Versionen von iOS kompatibel ist.

Wie auch immer, ich hoffe es hilft ...

6
Suran

Wenn Sie etwas Kompliziertes animieren möchten, ist es am besten, einen Screenshot davon programmgesteuert aufzunehmen und stattdessen zu animieren!

Um einen Screenshot einer Ansicht zu erstellen, benötigen Sie einen Code, der ungefähr so ​​aussieht:

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

Wobei mainContentView die Ansicht ist, von der Sie einen Screenshot machen möchten. Fügen Sie viewImage zu einer UIImageView hinzu und animieren Sie diese.

Hoffe das beschleunigt deine Animation !!

N

4
Nick Cartwright

Wie MuscleRumble erwähnt, ist der Rand der akzeptierten Antwort etwas außermittig. Ich konnte dies korrigieren, indem ich die Strichstärke auf Null stellte, anstatt die Farbe auf Löschen zu ändern.

d.h. ersetzen:

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

mit:

CGContextSetTextDrawingMode(c, kCGTextFillStroke);
self.textColor = textColor;
CGContextSetLineWidth(c, 0); // set stroke width to zero
self.shadowOffset = CGSizeMake(0, 0);
[super drawTextInRect:rect];

Ich hätte nur seine Antwort kommentiert, aber anscheinend bin ich nicht "seriös" genug.

3
ufmike

A Swift 4 Klassenversion basierend auf dem Antwort von 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
    }
}

Sie kann vollständig im Interface Builder implementiert werden, indem Sie die benutzerdefinierte Klasse von UILabel auf OutlinedText setzen. Anschließend können Sie die Breite und Farbe des Umrisses im Eigenschaftenfenster festlegen.

enter image description here

2
Chris Stillwell

wenn ALLES, was Sie wollen, ein schwarzer Rand von einem Pixel um meinen weißen UILabel-Text ist,

dann denke ich, dass Sie das Problem schwieriger machen als es ist ... Ich weiß nicht auswendig, welche 'draw rect/frameRect'-Funktion Sie verwenden sollten, aber es wird für Sie leicht zu finden sein. Diese Methode demonstriert nur die Strategie (lassen Sie die Superklasse die Arbeit erledigen!):

- (void)drawRect:(CGRect)rect
{
  [super drawRect:rect];
  [context frameRect:rect]; // research which rect drawing function to use...
}
2
kent

Ich habe ein Problem mit der Hauptantwort gefunden. Die Textposition muss nicht unbedingt korrekt auf die Position unterhalb der Pixel zentriert sein, damit der Umriss nicht mit dem Text übereinstimmt. Ich habe es mit dem folgenden Code behoben, der CGContextSetShouldSubpixelQuantizeFonts(ctx, false) verwendet:

- (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];
}

Dies setzt voraus, dass Sie textOutlineColor und textOutlineWidth als Eigenschaften definiert haben.

1
Robotbugs

Hier ist die andere Antwort, um den umrissenen Text auf dem Etikett festzulegen.

extension UILabel {

func setOutLinedText(_ text: String) {
    let attribute : [NSAttributedString.Key : Any] = [
        NSAttributedString.Key.strokeColor : UIColor.black,
        NSAttributedString.Key.foregroundColor : UIColor.white,
        NSAttributedString.Key.strokeWidth : -2.0,
        NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 12)
        ] as [NSAttributedString.Key  : Any]

    let customizedText = NSMutableAttributedString(string: text,
                                                   attributes: attribute)
    attributedText = customizedText
 }

}

stellen Sie den umrissenen Text einfach mit der Erweiterungsmethode ein.

lblTitle.setOutLinedText("Enter your email address or username")
0
Aman Pathak

es ist auch möglich, UILabel mit der folgenden Logik zu unterklassifizieren:

- (void)setText:(NSString *)text {
    [self addOutlineForAttributedText:[[NSAttributedString alloc] initWithString:text]];
}

- (void)setAttributedText:(NSAttributedString *)attributedText {
    [self addOutlineForAttributedText:attributedText];
}

- (void)addOutlineForAttributedText:(NSAttributedString *)attributedText {
    NSDictionary *strokeTextAttributes = @{
                                           NSStrokeColorAttributeName: [UIColor blackColor],
                                           NSStrokeWidthAttributeName : @(-2)
                                           };

    NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText];
    [attrStr addAttributes:strokeTextAttributes range:NSMakeRange(0, attrStr.length)];

    super.attributedText = attrStr;
}

und wenn Sie Text in Storyboard setzen, dann:

- (instancetype)initWithCoder:(NSCoder *)aDecoder {

    self = [super initWithCoder:aDecoder];
    if (self) {
        // to apply border for text from storyboard
        [self addOutlineForAttributedText:[[NSAttributedString alloc] initWithString:self.text]];
    }
    return self;
}
0
dollar2048