Làm cách nào để kiểm tra xem UILabel có bị cắt bớt hay không?


105

Tôi có một UILabelđộ dài có thể thay đổi tùy thuộc vào việc ứng dụng của tôi có đang chạy ở chế độ dọc hoặc ngang trên iPhone hoặc iPad hay không. Khi văn bản quá dài để hiển thị trên một dòng và nó bị cắt ngắn, tôi muốn người dùng có thể nhấn vào nó và nhận được một cửa sổ bật lên của toàn văn.

Làm cách nào để kiểm tra xem UILabelvăn bản có bị cắt bớt hay không? Nó thậm chí có thể? Hiện tại, tôi chỉ đang kiểm tra các độ dài khác nhau dựa trên chế độ tôi đang ở nhưng nó hoạt động không tốt.


1
Hãy xem giải pháp dựa trên số dòng mà tôi đã đăng ở đây
Claus

Không thể tin rằng sau ngần ấy năm Apple vẫn chưa kết hợp một thứ gì đó cơ bản như thế này vào UILabelAPI.
funct7

Câu trả lời:


108

Bạn có thể tính toán chiều rộng của chuỗi và xem liệu chiều rộng có lớn hơnlabel.bounds.size.width

NSString UIKit Additions có một số phương pháp để tính toán kích thước của chuỗi với một phông chữ cụ thể. Tuy nhiên, nếu bạn có Kích thước tối thiểu cho nhãn của mình cho phép hệ thống thu nhỏ văn bản xuống kích thước đó. Bạn có thể muốn sử dụng sizeWithFont: minFontSize: realFontSize: forWidth: lineBreakMode: trong trường hợp đó.

CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
   ...
}

1
Cảm ơn, đây chính xác là những gì tôi cần. Sự khác biệt duy nhất là sizeWithFont: trả về một CGSize.
Randall

À, cảm ơn bạn đã chỉ ra điều đó, tôi đã sửa mã mẫu.
progrmr

16
sizeWithFont không được sử dụng nữa: [label.text sizeWithAttributes: @ {NSFontAttributeName: label.font}];
Amelia777

2
@fatuhoku numberOfLinestrả về số dòng tối đa được sử dụng để hiển thị văn bản như được nêu trong UILabeltham chiếu lớp: developer.apple.com/library/ios/documentation/UIKit/Reference/…
Paul

1
nếu nhãn có số đường thử nhân rộng với số dòng như thế này if (size.width> label.bounds.size.width * label.numberOfLines) {...}
Fadi Abuzant

89

Swift (dưới dạng phần mở rộng) - hoạt động cho uilabel nhiều dòng:

swift4: (thông attributessố boundingRectthay đổi một chút)

extension UILabel {

    var isTruncated: Bool {

        guard let labelText = text else {
            return false
        }

        let labelTextSize = (labelText as NSString).boundingRect(
            with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
            options: .usesLineFragmentOrigin,
            attributes: [.font: font],
            context: nil).size

        return labelTextSize.height > bounds.size.height
    }
}

nhanh chóng3:

extension UILabel {

    var isTruncated: Bool {

        guard let labelText = text else { 
            return false
        }

        let labelTextSize = (labelText as NSString).boundingRect(
            with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
            options: .usesLineFragmentOrigin,
            attributes: [NSFontAttributeName: font],
            context: nil).size

        return labelTextSize.height > bounds.size.height
    }
}

swift2:

extension UILabel {

    func isTruncated() -> Bool {

        if let string = self.text {

            let size: CGSize = (string as NSString).boundingRectWithSize(
                CGSize(width: self.frame.size.width, height: CGFloat(FLT_MAX)),
                options: NSStringDrawingOptions.UsesLineFragmentOrigin,
                attributes: [NSFontAttributeName: self.font],
                context: nil).size

            if (size.height > self.bounds.size.height) {
                return true
            }
        }

        return false
    }

}

thay thế chiều cao 999999.0 bằng CGFloat (FLT_MAX)
ambientlight

2
đối với nhanh 3, tôi sẽ sử dụng CGFloat.greatestFiniteMagosystem
zero3nna

2
câu trả lời hay nhưng tốt hơn nên sử dụng thuộc tính được tính toán thay vì func: var isTruncated: Bool {if let string = self.text {let size: CGSize = (string as NSString) .boundsRect (with: CGSize (width: self.frame.size . width, height: CGFloat.greatestFiniteMagosystem), tùy chọn: NSStringDrawingOptions.usesLineFragmentOrigin, các thuộc tính: [NSFontAttributeName: self.font], context: nil) .size return (size.height> self.bounds.size.height)} return false}
Mohammadalijf

1
Điều này không hiệu quả với tôi vì tôi đang sử dụng NSAttributedString. Để có được nó để làm việc, tôi cần phải thay đổi các giá trị thuộc tính để sử dụng: attributedText .attributes (tại địa chỉ: 0, effectiveRange: nil)?
pulse4life

1
Câu trả lời tuyệt vời, đã phải thay đổi nó một chút cho yêu cầu của tôi, nhưng hoạt động hoàn hảo. Tôi cũng đang sử dụng một chuỗi phân bổ - vì vậy, đối với các thuộc tính tôi đã sử dụng: (doedText? .Attributes (at: 0, effectRange: nil) ?? [.font: font]), chỉ cần đảm bảo kiểm tra xem labelText có trống không khi sử dụng giải pháp này.
Crt Gregoric

20

CHỈNH SỬA: Tôi vừa thấy câu trả lời của mình đã được ủng hộ, nhưng đoạn mã tôi đưa ra không được dùng nữa.
Bây giờ cách tốt nhất để làm điều này là (ARC):

NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = mylabel.lineBreakMode;
NSDictionary *attributes = @{NSFontAttributeName : mylabel.font,
                             NSParagraphStyleAttributeName : paragraph};
CGSize constrainedSize = CGSizeMake(mylabel.bounds.size.width, NSIntegerMax);
CGRect rect = [mylabel.text boundingRectWithSize:constrainedSize
                                         options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                      attributes:attributes context:nil];
if (rect.size.height > mylabel.bounds.size.height) {
    NSLog(@"TOO MUCH");
}

Lưu ý kích thước được tính toán không phải là giá trị số nguyên. Vì vậy, nếu bạn làm những việc như vậy int height = rect.size.height, bạn sẽ mất đi một số độ chính xác của dấu phẩy động và có thể có kết quả sai.

Câu trả lời cũ (không dùng nữa):

Nếu nhãn của bạn là nhiều dòng, bạn có thể sử dụng mã này:

CGSize perfectSize = [mylabel.text sizeWithFont:mylabel.font constrainedToSize:CGSizeMake(mylabel.bounds.size.width, NSIntegerMax) lineBreakMode:mylabel.lineBreakMode];
if (perfectSize.height > mylabel.bounds.size.height) {
    NSLog(@"TOO MUCH");
}

13

bạn có thể tạo một danh mục với UILabel

- (BOOL)isTextTruncated

{
    CGRect testBounds = self.bounds;
    testBounds.size.height = NSIntegerMax;
    CGRect limitActual = [self textRectForBounds:[self bounds] limitedToNumberOfLines:self.numberOfLines];
    CGRect limitTest = [self textRectForBounds:testBounds limitedToNumberOfLines:self.numberOfLines + 1];
    return limitTest.size.height>limitActual.size.height;
}

3
từ tài liệu: textRectForBounds:limitedToNumberOfLines:"Bạn không nên gọi phương thức này trực tiếp" ...
Martin

@ Martin cảm ơn bạn, tôi thấy rằng, các thực hiện ở đây là limited.but khi bạn gọi phương pháp này sau khi cài đặt giới hạn và văn bản, nó sẽ làm việc tốt
Dongxu

Tại sao bạn +1 khi đặt limitedToNumberOfLines?
Tro

@Ash để kiểm tra xem nhãn có cao hơn không khi nó được phép có nhiều chỗ hơn cho văn bản.
vrwim

1
Cảm ơn vì mã này, nó đã hoạt động với tôi ngoài một số trường hợp đường viền khi sử dụng bố cục tự động. Tôi đã sửa chúng bằng cách thêm: setNeedsLayout() layoutIfNeeded()vào đầu phương thức.
Jovan Jovanovski

9

Sử dụng danh mục này để tìm xem một nhãn có bị cắt bớt trên iOS 7 trở lên hay không.

// UILabel+Truncation.h
@interface UILabel (Truncation)

@property (nonatomic, readonly) BOOL isTruncated;

@end


// UILabel+Truncation.m
@implementation UILabel (Truncation)

- (BOOL)isTruncated
{
    CGSize sizeOfText =
      [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)
                               options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                            attributes:@{ NSFontAttributeName : label.font } 
                               context: nil].size;

    if (self.frame.size.height < ceilf(sizeOfText.height))
    {
        return YES;
    }
    return NO;
}

@end

9

Swift 3

Bạn có thể đếm số dòng sau khi gán chuỗi và so sánh với số dòng tối đa của nhãn.

import Foundation
import UIKit

extension UILabel {

    func countLabelLines() -> Int {
        // Call self.layoutIfNeeded() if your view is uses auto layout
        let myText = self.text! as NSString
        let attributes = [NSFontAttributeName : self.font]

        let labelSize = myText.boundingRect(with: CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil)
        return Int(ceil(CGFloat(labelSize.height) / self.font.lineHeight))
    }

    func isTruncated() -> Bool {

        if (self.countLabelLines() > self.numberOfLines) {
            return true
        }
        return false
    }
}

Đây là câu trả lời tốt đẹp cho sự nhanh chóng. Cảm ơn.
JimmyLee

8

Để thêm vào câu trả lời của iDev , bạn nên sử dụng intrinsicContentSizethay vì frame, để làm cho nó hoạt động cho Autolayout

- (BOOL)isTruncated:(UILabel *)label{
        CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.intrinsicContentSize.width, CGFLOAT_MAX)
                                                     options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                  attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size;

        if (self.intrinsicContentSize.height < ceilf(sizeOfText.height)) {
        return YES;
    }
    return NO;
}

Cảm ơn! Sử dụng nội tạiContentSize thay vì frame là giải pháp cho vấn đề của tôi khi chiều cao của UILabel thực sự đủ để vừa với văn bản, nhưng nó có số dòng hạn chế và do đó vẫn đang bị cắt bớt.
Anton Matosov

Đối với một số lý do, điều này đang trở lại NO ngay cả khi văn bản không phù hợp với nhãn
Cần Poyrazoğlu

6

Đây là nó. Điều này hoạt động với attributedText, trước khi trở lại đơn giản text, điều này rất có ý nghĩa đối với chúng tôi, những người xử lý nhiều họ phông chữ, kích cỡ và thậm chí là NSTextAttachments!

Hoạt động tốt với autolayout, nhưng rõ ràng là các ràng buộc phải được xác định và thiết lập trước khi chúng tôi kiểm tra isTruncated, nếu không, bản thân nhãn thậm chí không biết cách bố trí chính nó, vì vậy không có cách nào nó thậm chí có thể biết nếu nó bị cắt bớt.

Nó không hoạt động để tiếp cận vấn đề này chỉ với một đơn giản NSStringsizeThatFits. Tôi không chắc làm thế nào mọi người nhận được kết quả tích cực như vậy. BTW, như đã đề cập nhiều lần, việc sử dụng sizeThatFitskhông phải là lý tưởng chút nào vì nó có tính numberOfLinesđến kích thước kết quả, điều này đánh bại toàn bộ mục đích của những gì chúng ta đang cố gắng làm, bởi vì isTruncatednó sẽ luôn trả về falsebất kể nó có bị cắt bớt hay không.

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()

        let rectBounds = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
        var fullTextHeight: CGFloat?

        if attributedText != nil {
            fullTextHeight = attributedText?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, context: nil).size.height
        } else {
            fullTextHeight = text?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).size.height
        }

        return (fullTextHeight ?? 0) > bounds.size.height
    }
}

cái này hoạt động tốt cho tôi cảm ơn !! có bất kỳ cập nhật hoặc sửa đổi nào cho cái này không?
amodkanthe

Câu trả lời chính xác. Vấn đề duy nhất được quy là Văn bản không bao giờ là con số không, ngay cả khi bạn không đặt nó. Vì vậy, tôi đã triển khai điều này dưới dạng hai vars isTextTruncated và isAttributedTextTruncated.
Harris

@Harris nó nói rằng mặc định là không trong tệp uilabel.h
Lucas

@LucasChwe Tôi biết, nhưng nó không đúng trong thực tế. Bạn có thể thử nó cho chính mình và xem. fyi, forum.developer.apple.com/thread/118581
Harris

3

Đây là câu trả lời đã chọn trong Swift 3 (dưới dạng phần mở rộng). OP đã hỏi về nhãn 1 dòng. Nhiều câu trả lời nhanh mà tôi đã thử ở đây dành riêng cho các nhãn nhiều dòng và không gắn cờ chính xác trên các nhãn dòng đơn.

extension UILabel {
    var isTruncated: Bool {
        guard let labelText = text as? NSString else {
            return false
        }
        let size = labelText.size(attributes: [NSFontAttributeName: font])
        return size.width > self.bounds.width
    }
}

Câu trả lời của Axel không phù hợp với điều này. Điều này đã làm. Cảm ơn!
Glenn

2

Điều này hoạt động cho iOS 8:

CGSize size = [label.text boundingRectWithSize:CGSizeMake(label.bounds.size.width, NSIntegerMax) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil].size;

if (size.height > label.frame.size.height) {
    NSLog(@"truncated");
}

2

Tôi đã viết một chuyên mục để làm việc với việc cắt bớt của UILabel. Hoạt động trên iOS 7 trở lên. Hy vọng nó giúp ! cắt đuôi uilabel

@implementation UILabel (Truncation)

- (NSRange)truncatedRange
{
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[self attributedText]];

    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];

    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self bounds].size];
    textContainer.lineFragmentPadding = 0;
    [layoutManager addTextContainer:textContainer];

    NSRange truncatedrange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:0];
    return truncatedrange;
}

- (BOOL)isTruncated
{
    return [self truncatedRange].location != NSNotFound;
}

- (NSString *)truncatedText
{
    NSRange truncatedrange = [self truncatedRange];
    if (truncatedrange.location != NSNotFound)
    {
        return [self.text substringWithRange:truncatedrange];
    }

    return nil;
}

@end

mọi lúc nó chỉ cung cấp cho NSNotFound
Dari

2
extension UILabel {

public func resizeIfNeeded() -> CGFloat? {
    guard let text = text, !text.isEmpty else { return nil }

    if isTruncated() {
        numberOfLines = 0
        sizeToFit()
        return frame.height
    }
    return nil
}

func isTruncated() -> Bool {
    guard let text = text, !text.isEmpty else { return false }

    let size: CGSize = text.size(withAttributes: [NSAttributedStringKey.font: font])
    return size.width > self.bounds.size.width
    }
}

Bạn có thể tính toán chiều rộng của chuỗi và xem liệu chiều rộng có lớn hơn chiều rộng nhãn hay không.


1

Để thêm vào những gì @iDev đã làm, tôi đã sửa đổi self.frame.size.heightcách sử dụng label.frame.size.heightvà cũng không sử dụng NSStringDrawingUsesLineFontLeading. Sau những sửa đổi đó, tôi đã tính toán hoàn hảo về thời điểm cắt ngắn sẽ xảy ra (ít nhất là đối với trường hợp của tôi).

- (BOOL)isTruncated:(UILabel *)label {
    CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.bounds.size.width, CGFLOAT_MAX)
                                                 options: (NSStringDrawingUsesLineFragmentOrigin)
                                              attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size;

    if (label.frame.size.height < ceilf(sizeOfText.height)) {
        return YES;
    }
    return NO;
}

1

Tôi gặp sự cố boundingRect(with:options:attributes:context:)khi sử dụng tính năng tự động thanh toán (để đặt chiều cao tối đa) và văn bản được gán vớiNSParagraph.lineSpacing

Khoảng cách giữa các dòng đã bị bỏ qua (ngay cả khi được thông qua vào attributesđến boundingRectphương pháp) để nhãn có thể được coi là không cắt ngắn khi nó được.

Giải pháp tôi tìm thấy là sử dụng UIView.sizeThatFits:

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()
        let heightThatFits = sizeThatFits(bounds.size).height
        return heightThatFits > bounds.size.height
    }
}

0

Bởi vì tất cả các câu trả lời ở trên sử dụng phương pháp khấu hao, tôi nghĩ rằng điều này có thể hữu ích:

- (BOOL)isLabelTruncated:(UILabel *)label
{
    BOOL isTruncated = NO;

    CGRect labelSize = [label.text boundingRectWithSize:CGSizeFromString(label.text) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil];

    if (labelSize.size.width / labelSize.size.height > label.numberOfLines) {

        isTruncated = YES;
    }

    return isTruncated;
}

Bạn đang chia chiều rộng cho chiều cao và nếu nó lớn hơn số dòng (rất có thể là 0), bạn đang nói rằng nhãn bị cắt bớt. Không có cách nào điều này hoạt động.
Can Leloğlu

@ CanLeloğlu vui lòng kiểm tra nó. Đó là một ví dụ làm việc.
Raz

2
Điều gì sẽ xảy ra nếu numberOfLines bằng 0?
Can Leloğlu

0

Để xử lý iOS 6 (vâng, một số người trong chúng ta vẫn phải làm), đây là một bản mở rộng khác cho câu trả lời của @ iDev. Điều quan trọng là, đối với iOS 6, để đảm bảo rằng numberOfLines của UILabel của bạn được đặt thành 0 trước khi gọi sizeThatFits; nếu không, nó sẽ cung cấp cho bạn một kết quả cho biết "các điểm để vẽ giá trị chiều cao của numberOfLines" là cần thiết để vẽ văn bản nhãn.

- (BOOL)isTruncated
{
    CGSize sizeOfText;

    // iOS 7 & 8
    if([self.text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])
    {
        sizeOfText = [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)
                                             options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                          attributes:@{NSFontAttributeName:self.font}
                                             context:nil].size;
    }
    // iOS 6
    else
    {
        // For iOS6, set numberOfLines to 0 (i.e. draw label text using as many lines as it takes)
        //  so that siteThatFits works correctly. If we leave it = 1 (for example), it'll come
        //  back telling us that we only need 1 line!
        NSInteger origNumLines = self.numberOfLines;
        self.numberOfLines = 0;
        sizeOfText = [self sizeThatFits:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)];
        self.numberOfLines = origNumLines;
    }

    return ((self.bounds.size.height < sizeOfText.height) ? YES : NO);
}

0

Đảm bảo gọi một trong hai thứ này trong viewDidLayoutSubviews.

public extension UILabel {

    var isTextTruncated: Bool {
        layoutIfNeeded()
        return text?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font!], context: nil).size.height ?? 0 > bounds.size.height
    }

    var isAttributedTextTruncated: Bool {
        layoutIfNeeded()
        return attributedText?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size.height ?? 0 > bounds.size.height
    }

}

0

SWIFT 5

Ví dụ cho UILabel nhiều dòng được đặt để chỉ hiển thị 3 dòng.

    let labelSize: CGSize = myLabel.text!.size(withAttributes: [.font: UIFont.systemFont(ofSize: 14, weight: .regular)])

    if labelSize.width > myLabel.intrinsicContentSize.width * 3 {
        // your label will truncate
    }

Mặc dù người dùng có thể chọn phím quay lại thêm một dòng bổ sung mà không thêm vào "chiều rộng văn bản" trong trường hợp đó, một cái gì đó như thế này cũng có thể hữu ích.

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    if text == "\n" {
        // return pressed 

    }
}

-1

Giải pháp Swift 3

Tôi nghĩ giải pháp tốt nhất là (1) tạo một UILabelcó cùng thuộc tính với nhãn bạn đang kiểm tra để cắt bớt, (2) lệnh gọi .sizeToFit(), (3) so sánh các thuộc tính của nhãn giả với nhãn thực của bạn.

Ví dụ: nếu bạn muốn kiểm tra xem một nhãn lót có cắt bớt chiều rộng khác nhau hay không, thì bạn có thể sử dụng phần mở rộng này:

extension UILabel {
    func isTruncated() -> Bool {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: CGFloat.greatestFiniteMagnitude, height: self.bounds.height))
        label.numberOfLines = 1
        label.font = self.font
        label.text = self.text
        label.sizeToFit()
        if label.frame.width > self.frame.width {
            return true
        } else {
            return false
        }
    }
}

... nhưng một lần nữa, bạn có thể dễ dàng sửa đổi mã trên để phù hợp với nhu cầu của mình. Vì vậy, giả sử nhãn của bạn có nhiều đường và có chiều cao khác nhau. Sau đó, phần mở rộng sẽ trông giống như sau:

extension UILabel {
    func isTruncated() -> Bool {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude))
        label.numberOfLines = 0
        label.font = self.font
        label.text = self.text
        label.sizeToFit()
        if label.frame.height > self.frame.height {
            return true
        } else {
            return false
        }
    }
}

-6

Sẽ không dễ dàng để đặt thuộc tính tiêu đề cho nhãn, cài đặt này sẽ hiển thị nhãn đầy đủ khi di chuột.

bạn có thể tính chiều dài của nhãn và chiều rộng div (chuyển đổi thành chiều dài - jQuery / Javascript - Làm cách nào để chuyển đổi giá trị pixel (20px) thành giá trị số (20) ).

đặt jquery để đặt tiêu đề nếu chiều dài lớn hơn chiều rộng div.

var divlen = parseInt(jQuery("#yourdivid").width,10);
var lablen =jQuery("#yourlabelid").text().length;
if(lablen < divlen){
jQuery("#yourlabelid").attr("title",jQuery("#yourlabelid").text());
}
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.