Làm cách nào để hiển thị văn bản có viền ngoài của UILabel?


108

Tất cả những gì tôi muốn là một đường viền đen một pixel xung quanh văn bản UILabel màu trắng của tôi.

Tôi đã phân loại được UILabel với đoạn mã bên dưới, mà tôi đã tập hợp một cách vụng về từ một vài ví dụ trực tuyến có liên quan trực tiếp. Và nó hoạt động nhưng rất, rất chậm (ngoại trừ trên trình mô phỏng) và tôi cũng không thể làm cho nó căn giữa văn bản theo chiều dọc (vì vậy tôi tạm thời mã hóa giá trị y trên dòng cuối cùng). 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]);
}

Tôi chỉ có một cảm giác khó chịu rằng tôi đang nhìn ra một cách đơn giản hơn để làm điều này. Có lẽ bằng cách ghi đè "drawTextInRect", nhưng tôi dường như không thể làm cho drawTextInRect làm theo ý mình mặc dù nhìn chăm chú vào nó và cau mày thực sự rất khó.


Làm rõ - sự chậm chạp là điều hiển nhiên trong ứng dụng của tôi vì tôi đang tạo hiệu ứng cho nhãn khi giá trị của nó thay đổi với sự phát triển và thu nhỏ lại một chút. Nếu không có phân lớp thì nó rất mượt, nhưng với đoạn mã phía trên thì hoạt ảnh nhãn thật là khó hiểu. Tôi có nên sử dụng UIWebView không? Tôi cảm thấy ngớ ngẩn khi làm điều đó như nhãn chỉ hiển thị một số duy nhất ...
Monte Hurd

Được rồi, có vẻ như vấn đề hiệu suất mà tôi gặp phải không liên quan đến mã phác thảo, nhưng dường như tôi vẫn không thể làm cho nó căn chỉnh theo chiều dọc. pt.y luôn bằng 0 vì một số lý do.
Monte Hurd

Đây là rất chậm cho các phông chữ như Chalkduster
jjxtra

Câu trả lời:


164

Tôi đã có thể làm điều đó bằng cách ghi đè 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;

}

3
Tôi đã thử kỹ thuật này nhưng kết quả không như mong muốn. Trên iPhone 4, tôi đã thử sử dụng mã này để vẽ văn bản màu trắng với đường viền màu đen. Những gì tôi nhận được là các đường viền màu đen ở các cạnh trái và phải của mỗi chữ cái trong văn bản nhưng không có đường viền ở trên cùng hoặc dưới cùng. Bất kỳ ý tưởng?
Mike Hershberg

xin lỗi tôi đang cố gắng sử dụng mã của bạn cho dự án của tôi nhưng không có gì xảy ra! xem ảnh này: liên kết
Momi

@Momeks: Bạn phải phân lớp UILabelvà đặt việc -drawTextInRect:triển khai ở đó.
Jeff Kelley

1
@Momeks: Nếu bạn không biết cách tạo lớp con trong Objective-C, bạn nên thực hiện một số thao tác trên Google; Apple có một hướng dẫn về ngôn ngữ Objective-C.
Jeff Kelley

1
Có lẽ - tôi không nghĩ rằng điều đó tồn tại khi tôi viết bài này 2,5 năm trước :)
kprevas

114

Một giải pháp đơn giản hơn là sử dụng Chuỗi thuộc tính như sau:

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)

Trên một, UITextFieldbạn có thể đặt defaultTextAttributesattributedPlaceholdercả.

Lưu ý rằng NSStrokeWidthAttributeName phải phủ định trong trường hợp này, tức là chỉ những đường viền bên trong mới hoạt động.

UITextFields với một dàn ý sử dụng các văn bản được gán


17
Để thuận tiện trong Objective-C, các thuộc tính này sẽ là: label.attributedText = [[NSAttributedString Distribution] initWithString: @ "String" thuộc tính: @ {NSStrokeColorAttributeName: [UIColor blackColor], NSForegroundColorAttributeName: [UIColor whiteColor], NSStribStroke }Width: @ ;
Leszek Szary

8
Việc sử dụng meme này truyền đạt một cách hiệu quả các sắc thái của phương pháp này
woody121

Swift 4.2: hãy strokeTextAttributes: [String: Bất kỳ] = [NSStrokeColorAttributeName: UIColor.black, NSForegroundColorAttributeName: UIColor.white, NSStrokeWidthAttributeName: -4.0,] consoleView.typingAttributes = strokeTextAttributes
Teo Sartori

Cảm ơn bạn @TeoSartori, tôi đã thêm cú pháp Swift 4.2 trong câu trả lời của mình;)
Axel Guilmin 11/09/18

25

Sau khi đọc câu trả lời được chấp nhận và hai bản sửa chữa cho nó và câu trả lời từ Axel Guilmin, tôi quyết định biên soạn một giải pháp tổng thể bằng Swift, phù hợp với tôi:

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

Bạn có thể thêm lớp UILabel tùy chỉnh này vào nhãn hiện có trong Trình tạo giao diện và thay đổi độ dày của đường viền và màu của nó bằng cách thêm Thuộc tính thời gian chạy do người dùng xác định như sau: Thiết lập trình tạo giao diện

Kết quả:

chữ G lớn màu đỏ có viền


1
Đẹp. Tôi sẽ làm một IBInspectable với :) này
Mário Carvalho

@Lachezar làm cách nào để thực hiện điều này với văn bản của UIButton?
CrazyOne

8

Có một vấn đề với việc triển khai câu trả lời. Vẽ văn bản bằng nét có độ rộng glyph của ký tự hơi khác so với vẽ văn bản không có nét, có thể tạo ra kết quả "không có căn giữa". Nó có thể được sửa bằng cách thêm một nét vô hình xung quanh văn bản điền.

Thay thế:

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

với:

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

Tôi không chắc chắn 100%, nếu đó là giao dịch thực sự, bởi vì tôi không biết liệu nó self.textColor = textColor;có tác dụng tương tự hay không [textColor setFill], nhưng nó sẽ hoạt động.

Tiết lộ: Tôi là nhà phát triển của THLabel.

Tôi đã phát hành một lớp con UILabel cách đây không lâu, lớp này cho phép phác thảo văn bản và các hiệu ứng khác. Bạn có thể tìm thấy nó ở đây: https://github.com/tobihagemann/THLabel


4
chỉ được sử dụng đủ THLabel, cuộc sống trở nên phức tạp vì nó là
Prat

1
Nhờ THLabel, tôi đã có thể vẽ đường viền xung quanh văn bản chỉ bằng cách thêm hai dòng mã. Cảm ơn bạn đã cung cấp giải pháp cho vấn đề mà tôi đã dành quá nhiều thời gian để giải quyết!
Alan Kinnaman

Tôi ngạc nhiên là không ai để ý, nhưng lệnh Stroke tích hợp trong văn bản, không tạo đường viền mà thay vào đó nó tạo ra một " inline " - tức là nét được vẽ ở bên trong các chữ cái, không phải ở bên ngoài. Với THLabel, chúng ta có thể chọn để nét vẽ thực sự ở bên ngoài. Bạn đã làm rất tốt!
lenooh

5

Thao tác này sẽ không tạo ra một đường viền, nhưng nó sẽ tạo bóng xung quanh văn bản và nếu bạn làm cho bán kính bóng đủ nhỏ, nó có thể giống như một đường viền.

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

Tôi không biết liệu nó có tương thích với các phiên bản iOS cũ hơn hay không.

Dù sao, tôi hy vọng nó sẽ giúp ...


9
Đây không phải là một đường viền một pixel xung quanh nhãn. Đây là bóng đổ đơn giản xuống dưới cùng.
Tim

1
đây là giải pháp sai. Lạy chúa ai đã đánh dấu nó ?!
Sam B

họ nói khá rõ ràng 'vạch ra', đây là một cái bóng. không trả lời câu hỏi
hitme

5

Phiên bản Swift 4 lớp dựa trên câu trả lời của 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
    }
}

Nó có thể được triển khai hoàn toàn trong Trình tạo giao diện bằng cách đặt lớp tùy chỉnh của UILabel thành Out nétText. Sau đó, bạn sẽ có khả năng đặt chiều rộng và màu sắc của đường viền từ ngăn Thuộc tính.

nhập mô tả hình ảnh ở đây


Hoàn hảo cho Swift 5 quá.
Antee86

4

Nếu bạn muốn tạo hoạt ảnh cho một thứ gì đó phức tạp, thì cách tốt nhất là lập trình để chụp ảnh màn hình của nó làm hoạt ảnh!

Để chụp ảnh màn hình một chế độ xem, bạn sẽ cần mã như sau:

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

Trong đó mainContentView là chế độ xem bạn muốn chụp ảnh màn hình. Thêm viewImage vào UIImageView và tạo hiệu ứng động cho nó.

Hy vọng rằng tăng tốc hoạt ảnh của bạn !!

N


Đó là một thủ thuật tuyệt vời mà tôi có thể nghĩ ra một vài nơi để sử dụng và có thể giải quyết được vấn đề về hiệu suất, nhưng tôi vẫn hy vọng rằng có một cách đơn giản hơn lớp con tồi tệ của tôi để làm cho văn bản uilabel hiển thị đường viền. Trong thời gian này, tôi sẽ chơi với ví dụ của bạn, cảm ơn!
Monte Hurd

Tôi cảm nhận được nỗi đau của bạn. Tôi không chắc bạn sẽ tìm ra cách tốt để làm điều đó. Tôi thường viết nhiều đoạn mã cho các hiệu ứng sản phẩm sau đó chụp nhanh chúng (như trên) để có hoạt ảnh mượt mà! Đối với ví dụ trên, bạn cần bao gồm <QuartzCore / QuartzCore.h> trong mã của mình! May mắn nhất! N
Nick Cartwright, 09/07/09

4

Như MuscleRumble đã đề cập, đường viền của câu trả lời được chấp nhận hơi lệch tâm. Tôi đã có thể sửa lỗi này bằng cách đặt chiều rộng nét vẽ bằng 0 thay vì thay đổi màu thành rõ ràng.

tức là thay thế:

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

với:

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

Tôi sẽ chỉ bình luận về câu trả lời của anh ấy nhưng dường như tôi không đủ "uy tín".


2

nếu TẤT CẢ bạn muốn là một đường viền đen một pixel xung quanh văn bản UILabel màu trắng của tôi,

thì tôi thực sự nghĩ rằng bạn đang làm cho vấn đề khó khăn hơn nó ... Tôi không biết bạn nên sử dụng hàm 'draw direct / frameRect' nào, nhưng bạn sẽ dễ dàng tìm thấy. phương thức này chỉ thể hiện chiến lược (để lớp cha thực hiện công việc!):

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

tôi không hiểu. có lẽ vì tôi đã nhìn chằm chằm vào màn hình trong khoảng 12 giờ và tôi không nhận được nhiều vào thời điểm này ...
Monte Hurd

bạn đang phân lớp UILabel, một lớp đã vẽ văn bản. và lý do bạn phân lớp là vì bạn muốn thêm viền đen xung quanh văn bản. vì vậy nếu bạn để lớp cha vẽ văn bản, thì tất cả những gì bạn phải làm là vẽ đường viền, phải không?
kent

Đây là một điểm tốt ... mặc dù, nếu anh ta muốn thêm đường viền đen 1 pixel, lớp con có thể sẽ vẽ các chữ cái quá gần nhau! .... Tôi sẽ làm điều gì đó giống như đã đăng trong câu hỏi ban đầu và tối ưu hóa (như đã nêu trong bài đăng của tôi)! N
Nick Cartwright, 09/07/09

@nickcartwright: nếu trường hợp xảy ra mà lớp cha hoạt động như bạn nói, bạn có thể chèn CGRect trước khi gọi [super drawRect]. vẫn dễ dàng và an toàn hơn là viết lại chức năng của lớp cha.
kent

Và sau đó @kent rời SO trong sự thất vọng. Câu trả lời tuyệt vời, +1.
Dan Rosenstark vào

1

Tôi tìm thấy một vấn đề với câu trả lời chính. Vị trí văn bản không nhất thiết phải được căn giữa một cách chính xác với vị trí pixel phụ, do đó đường viền có thể không khớp xung quanh văn bản. Tôi đã sửa nó bằng cách sử dụng mã sau, sử dụng 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];
}

Điều này giả định rằng bạn đã xác định textOutlineColortextOutlineWidthlà thuộc tính.


0

Đây là một câu trả lời khác để đặt văn bản có viền trên nhãn.

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
 }

}

đặt văn bản có dàn ý chỉ đơn giản bằng cách sử dụng phương pháp mở rộng.

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

0

cũng có thể phân lớp UILabel với logic sau:

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

và nếu bạn đặt văn bản trong Bảng phân cảnh thì:

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

Nếu mục tiêu của bạn là như thế này:

nhập mô tả hình ảnh ở đây

Đây là cách tôi đạt được điều đó: Tôi đã thêm một labellớp tùy chỉnh mới làm Chế độ xem phụ vào UILabel hiện tại của mình (lấy cảm hứng từ câu trả lời này ).

Chỉ cần sao chép và dán nó vào dự án của bạn và bạn đã sẵn sàng:

extension UILabel {
    func addTextOutline(usingColor outlineColor: UIColor, outlineWidth: CGFloat) {
        class OutlinedText: UILabel{
            var outlineWidth: CGFloat = 0
            var outlineColor: UIColor = .clear

            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.textAlignment = .center
                self.textColor = outlineColor
                super.drawText(in:rect)

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

                self.shadowOffset = shadowOffset
            }
        }

        let textOutline = OutlinedText()
        let outlineTag = 9999

        if let prevTextOutline = viewWithTag(outlineTag) {
            prevTextOutline.removeFromSuperview()
        }

        textOutline.outlineColor = outlineColor
        textOutline.outlineWidth = outlineWidth
        textOutline.textColor = textColor
        textOutline.font = font
        textOutline.text = text
        textOutline.tag = outlineTag

        sizeToFit()
        addSubview(textOutline)
        textOutline.frame = CGRect(x: -(outlineWidth / 2), y: -(outlineWidth / 2),
                                   width: bounds.width + outlineWidth,
                                   height: bounds.height + outlineWidth)
    }
}

SỬ DỤNG:

yourLabel.addTextOutline(usingColor: .red, outlineWidth: 6)

nó cũng hoạt động cho a UIButtonvới tất cả các hoạt ảnh của nó:

yourButton.titleLabel?.addTextOutline(usingColor: .red, outlineWidth: 6)

-3

Tại sao bạn không tạo UIView đường viền 1px trong Photoshop, sau đó đặt UIView với hình ảnh và đặt nó phía sau UILabel của bạn?

Mã:

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

Nó sẽ giúp bạn tiết kiệm phân lớp một đối tượng và nó khá đơn giản để thực hiện.

Chưa kể nếu bạn muốn làm hoạt hình, nó được tích hợp sẵn trong lớp UIView.


Văn bản trên nhãn thay đổi và tôi cần văn bản đó có đường viền đen 1 pixel. (Phần còn lại của nền của UILabel là trong suốt.)
Monte Hurd

Tôi nghĩ rằng tôi đã hiểu cách điều này sẽ làm cho bất kỳ văn bản nào tôi đặt trong Nhãn được phác thảo, nhưng tôi đã rất mệt mỏi và bây giờ tôi dường như không thể tập trung vào việc làm thế nào nó có thể thực hiện được điều đó. Tôi sẽ đọc trên initWithPatternImage.
Monte Hurd

-3

Để đặt đường viền với các cạnh tròn xung quanh Nhãn dán, tôi thực hiện như sau:

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

(đừng quên bao gồm QuartzCore / QuartzCore.h)


5
Điều này thêm một biên giới lớn xung quanh toàn bộ nhãn, không có mặt ở văn bản
Pavel Alexeev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.