boundingRectWithSize สำหรับ NSAttributedString คืนขนาดผิด


151

ฉันพยายามรับค่า rect สำหรับสตริงที่ประกอบ แต่การเรียก boundingRectWithSize ไม่เคารพขนาดที่ฉันส่งเข้าไปและกำลังคืนค่า rect ที่มีความสูงบรรทัดเดียวซึ่งตรงข้ามกับความสูงขนาดใหญ่ (เป็นสตริงที่ยาว) ฉันได้ทดลองโดยการส่งผ่านค่าที่สูงมากสำหรับความสูงและ 0 เช่นเดียวกับในรหัสด้านล่าง

CGRect paragraphRect = [attributedText boundingRectWithSize:CGSizeMake(300,0.0)
  options:NSStringDrawingUsesDeviceMetrics
  context:nil];

สิ่งนี้ขาดหรือฉันต้องทำอย่างอื่นเพื่อให้มันคืนค่า rect สำหรับข้อความที่ถูกห่อหรือไม่?


5
คุณมีรูปแบบย่อหน้าที่มีการตัด / ตัดlineBreakModeหรือไม่?
danyowdee

1
ถ้าคุณกำลังอ่านนี้เพราะ UILabel วัด / ห่อเพื่อความกว้างไม่ถูกต้องจะดูที่stackoverflow.com/questions/46200027/... โดยเฉพาะการตั้งค่า NSAllowsDefaultLineBreakStrategy เป็น false เมื่อเปิดใช้แอป
eric

คำตอบ:


312

ดูเหมือนว่าคุณไม่ได้ให้ตัวเลือกที่ถูกต้อง สำหรับการห่อฉลากให้ระบุอย่างน้อย:

CGRect paragraphRect =
  [attributedText boundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)
  options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
  context:nil];

หมายเหตุ: หากความกว้างข้อความต้นฉบับต่ำกว่า 300.f จะไม่มีการตัดบรรทัดดังนั้นโปรดตรวจสอบให้แน่ใจว่าขนาดที่ถูกต้องถูกผูกไว้มิฉะนั้นคุณจะยังได้รับผลลัพธ์ที่ผิด


25
ทุกคนต้องดูคำตอบนี้เพราะมันใช้งานได้ บางทีอาจจำเป็นต้องใช้เอกสารประกอบที่ชัดเจนกว่าสำหรับวิธีนี้และสตริงที่มีการระบุโดยทั่วไป ไม่ชัดเจนว่าคุณต้องมองที่ไหน
macserv

31
ความสนใจ! มันไม่ได้ผลเสมอไป! บางครั้งความกว้างที่ส่งคืนจะใหญ่กว่าความกว้างของพารามิเตอร์ขนาด แม้แต่เอกสารคู่มือวิธีการของแอปเปิ้ล: "ข้อ จำกัด ที่คุณระบุในพารามิเตอร์ขนาดเป็นแนวทางสำหรับ renderer สำหรับวิธีกำหนดขนาดสตริงอย่างไรก็ตามสี่เหลี่ยมผืนผ้าขอบเขตที่แท้จริงที่ส่งคืนโดยวิธีนี้อาจมีขนาดใหญ่กว่าข้อ จำกัด หากต้องการพื้นที่เพิ่มเติม แสดงผลสตริงทั้งหมด "
Klaas

75
API สำหรับ NSAttributedString และ boundingRectWithSize เป็นเรื่องที่น่าตกใจอย่างยิ่ง
malhal

27
อีก gotcha ก็คือความสูง (และความกว้างคงสันนิษฐาน) กลับมาในวรรค Rect เกือบจะเป็นค่าเศษส่วน ดังนั้นมันอาจจะออกมาบอกว่า 141.3 เป็นความสูง คุณต้องใช้ผลจากการceilf(paragraphRect.size.height)ที่มันจะปัดเศษขึ้น ฉันลืมสิ่งนี้ตลอดเวลาและสงสัยว่าทำไมฉลากของฉันยังคงถูกตัด
jamone

39
ฉันมักจะboundingRectWithSize:คำนวณการคำนวณของฉันใน CGRectIntegral () ซึ่งCGRectIntegral rounds the rectangle’s origin downward and its size upward to the nearest whole integersในกรณีนี้จะปัดเศษความสูงและความกว้างขึ้นเพื่อให้แน่ใจว่าไม่มีการตัดเกิดขึ้นหากความสูงหรือความกว้างเป็นค่าเศษส่วน
runmad

47

ด้วยเหตุผลบางอย่าง boundingRectWithSize จะส่งคืนขนาดที่ผิดเสมอ ฉันหาวิธีแก้ปัญหา มีวิธีการสำหรับ UItextView -sizeThatFits ซึ่งส่งคืนขนาดที่เหมาะสมสำหรับชุดข้อความ ดังนั้นแทนที่จะใช้ boundingRectWithSize ให้สร้าง UITextView พร้อมเฟรมแบบสุ่มและเรียกขนาดของมันว่า FitFits ด้วยความกว้างและความสูง CGFLOAT_MAX ที่เกี่ยวข้อง ส่งคืนขนาดที่จะมีความสูงที่เหมาะสม

   UITextView *view=[[UITextView alloc] initWithFrame:CGRectMake(0, 0, width, 10)];   
   view.text=text;
   CGSize size=[view sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
   height=size.height; 

หากคุณกำลังคำนวณขนาดในขณะที่วงอย่าลืมที่จะเพิ่มในสระว่ายน้ำอัตโนมัติเนื่องจากจะมีจำนวน UITextView สร้างขึ้นจำนวนหน่วยความจำเวลาการทำงานของแอปจะเพิ่มขึ้นหากเราไม่ใช้ Autoreleasepool


1
วิธีนี้ใช้ได้ผล วิธีอื่น ๆ ที่ฉันพยายามฉันไม่สามารถทำงานได้ ฉันคิดว่าปัญหาของฉันเกิดจากความซับซ้อนของสายอักขระที่ฉันได้รับมอบหมาย มันมาจากไฟล์ RTF และใช้ฟอนต์ที่หลากหลายการเว้นวรรคบรรทัดแม้แต่เงาและแม้จะใช้ UsesLineFragmentOrigin และ UsesFontLeading ฉันจะไม่ได้รับผลลัพธ์ที่สูงพอ ฉันเดาว่าสำหรับสตริงที่ค่อนข้างง่ายวิธีการ boundingRectWithSize อาจทำงานได้ พวกเขาต้องการวิธีที่ดีกว่าในการทำงานกับสิ่งนี้ imo
John Bushnell

คุณไม่คิดว่ามันเป็น overkill ในการสร้างUITextViewเวลามากที่สุดเท่าที่heightForRowAtIndexPathวิธีการได้รับเรียก? มันต้องใช้เวลา
János

ฉันเห็นด้วยกับคุณ @ János แต่นี่เป็นทางออกเดียวที่เหมาะกับฉัน
shoan

btw ฉันขอวิธีแก้ปัญหาที่เหมาะสม: stackoverflow.com/questions/32495744/…
János

นี่เป็นทางออกเดียวที่ใช้งานได้ ปัญหานี้มี 9 ปีและเห็นได้ชัดว่า Apple ไม่ต้องการแก้ปัญหานี้หรือวิศวกรของพวกเขาอ่อนแอที่จะหาทางแก้ไขปัญหานี้ เรากำลังพูดถึงที่นี่เกี่ยวกับ iOS 11 ยังคงมีข้อผิดพลาดเดียวกัน
Duck

31

Ed McManus ได้มอบกุญแจในการทำให้สิ่งนี้เป็นจริง ฉันพบกรณีที่ใช้งานไม่ได้

UIFont *font = ...
UIColor *color = ...
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                     font, NSFontAttributeName,
                                     color, NSForegroundColorAttributeName,
                                     nil];

NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString: someString attributes:attributesDictionary];

[string appendAttributedString: [[NSAttributedString alloc] initWithString: anotherString];

CGRect rect = [string boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];

rectจะไม่มีความสูงที่ถูกต้อง ขอให้สังเกตว่าelseString (ซึ่งต่อท้ายกับสตริง ) ถูกเตรียมใช้งานโดยไม่มีพจนานุกรมแอตทริบิวต์ นี่เป็นตัวกำหนดค่าเริ่มต้นที่ถูกต้องสำหรับสายอื่นแต่การผูกขอบเขตแบบ: ขนาดไม่ได้ให้ขนาดที่ถูกต้องในกรณีนี้


33
ขอบคุณ! ปรากฎว่าส่วนหนึ่งของ NSAttributedString ทุกคนต้องมีชุดพจนานุกรมที่มีอย่างน้อย NSFontAttributeName และชุด NSForegroundColorAttributeName ถ้าคุณต้องการ boundingRectWithSize เพื่อใช้งานได้จริง! ฉันไม่เห็นเอกสารนั้นเลย
Ben Wheeler

3
โปรดทราบว่ามันยังปรากฏว่า NSAttributedStrings ที่แตกต่างกันที่รวมกันเพื่อสร้างแบบเดี่ยวทั้งหมดต้องใช้พจนานุกรม SAME (และแบบอักษรเดียวกัน) เพื่อให้ boundingRectWithSize ทำงานอย่างถูกต้อง!
Ben Wheeler

1
โซลูชันนี้คล้ายกับที่ฉันใช้สำหรับหมวดหมู่ UILabel ของฉันสำหรับ "ลดขนาดให้พอดี" กุญแจสำคัญสำหรับฉันในการทำงานนี้คือการสร้างสี่เหลี่ยมผืนผ้าที่มีขอบเขตด้วยความสูง FLT_MAX หากปราศจากสิ่งนั้นฉันก็ดูเหมือนจะได้สี่เหลี่ยมหนึ่งบรรทัด
dre1138

+1 ทุกรอบ นี่ทำให้ฉันได้จริงๆขอบคุณมากสำหรับการจัดการกับพวกคุณ
Wex

ฉันมีปัญหานี้ ฉันใช้ช่องว่างและบรรทัดใหม่NSMutableAttributedStringโดยไม่มีแอตทริบิวต์และฉันเริ่มมีขนาดผิด
vbezhenar

28

การตัดสินใจครั้งสุดท้ายของฉันหลังจากการตรวจสอบเป็นเวลานาน:
- boundingRectWithSizeฟังก์ชั่นคืนค่าขนาดที่ถูกต้องสำหรับลำดับของตัวละครเท่านั้น! ในกรณีที่สตริงมีช่องว่างหรืออย่างอื่น (เรียกว่าแอปเปิ้ล "ร่ายมนตร์บางอัน") - มันเป็นไปไม่ได้ที่จะได้รับขนาดที่แท้จริงของ rect ที่จำเป็นในการแสดงข้อความ!
ฉันได้แทนที่ช่องว่างในสตริงด้วยตัวอักษรและได้ผลลัพธ์ที่ถูกต้องทันที

Apple พูดที่นี่: https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

"วิธีนี้จะคืนค่าขอบเขตที่แท้จริงของ glyphs ในสายอักขระ glyphs บางตัว (ช่องว่างเป็นต้น) ได้รับอนุญาตให้ซ้อนทับข้อ จำกัด โครงร่างที่ระบุโดยขนาดที่ส่งผ่านดังนั้นในบางกรณีค่าความกว้างของส่วนประกอบขนาดของ ที่ส่งคืนCGRectสามารถเกินค่าความกว้างของพารามิเตอร์ขนาดได้ "

ดังนั้นจึงจำเป็นต้องหาวิธีอื่นในการคำนวณ rect จริง ...


หลังจากการแก้ปัญหากระบวนการตรวจสอบนานพบในที่สุด !!! ฉันไม่แน่ใจว่ามันจะทำงานได้ดีสำหรับทุกกรณีที่เกี่ยวข้องUITextViewแต่ตรวจพบสิ่งที่สำคัญและสำคัญ!

boundingRectWithSizeฟังก์ชั่นเช่นเดียวกับCTFramesetterSuggestFrameSizeWithConstraints(และวิธีการอื่น ๆ อีกมากมาย) จะคำนวณขนาดและส่วนของข้อความที่ถูกต้องเมื่อใช้รูปสี่เหลี่ยมผืนผ้าที่ถูกต้อง ตัวอย่างเช่น - UITextViewมีtextView.bounds.size.width- และค่านี้ไม่ใช่สี่เหลี่ยมผืนผ้าจริงที่ใช้โดยระบบเมื่อมีการวาดข้อความUITextViewและค่านี้ไม่ได้เกิดขึ้นจริงสี่เหลี่ยมผืนผ้าใช้โดยระบบเมื่อข้อความวาดภาพบน

ฉันพบพารามิเตอร์ที่น่าสนใจและทำการคำนวณอย่างง่ายในโค้ด:

CGFloat padding = textView.textContainer.lineFragmentPadding;  
CGFloat  actualPageWidth = textView.bounds.size.width - padding * 2;

และงานเวทย์มนตร์ - ตำราทั้งหมดของฉันคำนวณถูกต้องแล้ว! สนุก!


1
ใช่ฉันสังเกตเห็นว่ามันไม่ได้ จำกัด ความกว้างมันก็ใหญ่ขึ้นเสมอ นั่นไม่เจ๋งจริงพวกเขาเลิกใช้วิธีการทำงานและตอนนี้เราต้องจัดการกับอึนี้
Boris Gafurov

1
คุณเป็นฮีโร่ใหม่ของฉัน! นี่ทำให้ฉันบ้า ฉันกำลังตั้งค่าตัวแทรก textContainers ดังนั้นจึงต้องใช้ textView.textContainer.size.width แทน textView.bounds.size.witdh
GCBenson

ฉันไม่สามารถลงคะแนนนี้พอ แปลกมากที่ช่องว่างไม่ได้ถูกพิจารณาในการคำนวณขอบเขต
Chase Holland

1
การแทนที่ช่องว่างด้วยตัวละครจำลองบางตัวเช่น "3" จะให้ความสูงที่แน่นอน
Abuzar Amin

1
สิ่งนี้ช่วยชีวิตฉันและความสัมพันธ์ของฉันกับทีม QA ฉันเป็นหนี้นายชายเบียร์ !!
lucaslt89

14

รวดเร็วสี่รุ่น

let string = "A great test string."
let font = UIFont.systemFont(ofSize: 14)
let attributes: [NSAttributedStringKey: Any] = [.font: font]
let attributedString = NSAttributedString(string: string, attributes: attributes)
let largestSize = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)

//Option one (best option)
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
let textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRange(), nil, largestSize, nil)

//Option two
let textSize = (string as NSString).boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], attributes: attributes, context: nil).size

//Option three
let textSize = attributedString.boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], context: nil).size

การวัดข้อความด้วย CTFramesetter นั้นทำงานได้ดีที่สุดเนื่องจากมีขนาดจำนวนเต็มและจัดการกับตัวอักษรอีโมจิและตัวอักษรยูนิโค้ดอื่น ๆ ได้ดี


10

ฉันไม่มีโชคกับคำแนะนำเหล่านี้ สายอักขระของฉันมีสัญลักษณ์แสดงหัวข้อย่อย Unicode และฉันสงสัยว่าพวกเขาก่อให้เกิดความเศร้าโศกในการคำนวณ ฉันสังเกตเห็นว่า UITextView จัดการการวาดภาพได้ดีดังนั้นฉันจึงมองว่าเพื่อใช้ประโยชน์จากการคำนวณ ฉันทำสิ่งต่อไปนี้ซึ่งอาจไม่ดีเท่าวิธีการวาด NSString แต่อย่างน้อยก็แม่นยำ นอกจากนี้ยังเล็กน้อยที่เหมาะสมกว่า initialising UITextView -sizeThatFits:เพียงเพื่อการโทร

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(width, CGFLOAT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:formattedString];
[textStorage addLayoutManager:layoutManager];

const CGFloat formattedStringHeight = ceilf([layoutManager usedRectForTextContainer:textContainer].size.height);

มันอาจจะไม่ใช่จุดยูนิโค้ด แต่ก็อาจมีช่องว่างที่ท้ายสตริงของคุณ ดูคำตอบด้านบน: stackoverflow.com/a/25941139/337934
SamB

9

ในกรณีที่คุณต้องการได้รับกล่องขอบเขตโดยการตัดหางคำถามนี้สามารถช่วยคุณได้

CGFloat maxTitleWidth = 200;

NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;

NSDictionary *attributes = @{NSFontAttributeName : self.textLabel.font,
                             NSParagraphStyleAttributeName: paragraph};

CGRect box = [self.textLabel.text
              boundingRectWithSize:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)
              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
              attributes:attributes context:nil];

9

ปรากฎว่าส่วนหนึ่งของ NSAttributedString ทุกคนต้องมีชุดพจนานุกรมที่มีอย่างน้อย NSFontAttributeName และชุด NSForegroundColorAttributeName ถ้าคุณต้องการ boundingRectWithSize เพื่อใช้งานได้จริง!

ฉันไม่เห็นเอกสารนั้นเลย


ขอบคุณนี่เป็นวิธีแก้ปัญหาสำหรับฉันยกเว้นฉันพบว่ามีเพียง NSFontAttributeName เท่านั้นที่จำเป็นไม่ใช่ NSForegroundColorAttributeName
Marmoy

7

@warrenm ขออภัยที่จะบอกว่าวิธี framesetter ใช้งานไม่ได้สำหรับฉัน

ฉันได้รับสิ่งนี้ฟังก์ชันนี้สามารถช่วยเรากำหนดขนาดเฟรมที่ต้องการสำหรับช่วงสตริงของ NSAttributedString ใน iphone / Ipad SDK สำหรับความกว้างที่กำหนด:

มันสามารถใช้สำหรับความสูงแบบไดนามิกของเซลล์ UITableView

- (CGSize)frameSizeForAttributedString:(NSAttributedString *)attributedString
{
    CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CGFloat width = YOUR_FIXED_WIDTH;

    CFIndex offset = 0, length;
    CGFloat y = 0;
    do {
        length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
        CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));

        CGFloat ascent, descent, leading;
        CTLineGetTypographicBounds(line, &ascent, &descent, &leading);

        CFRelease(line);

        offset += length;
        y += ascent + descent + leading;
    } while (offset < [attributedString length]);

    CFRelease(typesetter);

    return CGSizeMake(width, ceil(y));
}

ขอบคุณ HADDAD ISSA >>> http://haddadissa.blogspot.in/2010/09/compute-needed-heigh-for-fixed-width-of.html HTML


ใช้งานไม่ได้กับอุปกรณ์ใด ๆ (iPhone 4 และ 5) ทั้งบนอุปกรณ์ที่มี iOS7.1.2 และ 4s Simulator ด้วย iOS8.3 :(
sanjana

ต้องนำเข้าหรือไม่
jose920405

@ KaranAlangat ใช่#import <CoreText/CoreText.h>
jose920405

7

ฉันพบว่าโซลูชันที่ต้องการไม่รองรับการขึ้นบรรทัดใหม่

ฉันพบว่าวิธีการนี้ใช้ได้ผลในทุกกรณี:

UILabel* dummyLabel = [UILabel new];
[dummyLabel setFrame:CGRectMake(0, 0, desiredWidth, CGFLOAT_MAX)];
dummyLabel.numberOfLines = 0;
[dummyLabel setLineBreakMode:NSLineBreakByWordWrapping];
dummyLabel.attributedText = myString;
[dummyLabel sizeToFit];
CGSize requiredSize = dummyLabel.frame.size;

ในกรณีของฉันฉันควรทำการคำนวณบนเธรดพื้นหลังและไม่อนุญาตให้ใช้องค์ประกอบ UI
Lubbo

4

ฉันมีปัญหาเดียวกันกับการไม่ได้ขนาดที่ถูกต้องโดยใช้เทคนิคเหล่านี้และฉันได้เปลี่ยนวิธีการของฉันเพื่อให้ทำงานได้

ฉันมีสายอักขระที่มีความยาวซึ่งฉันพยายามที่จะใส่ลงใน scroll view เพื่อให้แสดงได้อย่างถูกต้องโดยไม่ถูกตัดทอน สิ่งที่ฉันทำเพื่อให้ข้อความทำงานได้อย่างน่าเชื่อถือคือไม่ได้กำหนดความสูงเลยว่าเป็นข้อ จำกัด และอนุญาตให้ใช้ขนาดที่แท้จริงแทนได้ ตอนนี้ข้อความถูกแสดงอย่างถูกต้องโดยไม่ถูกตัดทอนและฉันไม่จำเป็นต้องคำนวณความสูง

ฉันคิดว่าถ้าฉันต้องการความสูงอย่างน่าเชื่อถือฉันจะสร้างมุมมองที่ซ่อนอยู่และข้อ จำกัด เหล่านี้และรับความสูงของเฟรมเมื่อข้อ จำกัด ถูกนำไปใช้


2
คุณสามารถเพิ่มตัวอย่างรหัสเพื่อสาธิตสิ่งนี้ได้หรือไม่ ขอบคุณ.
Nathan Buggia

3

ฉันมีความล่าช้าเล็กน้อยในเกม - แต่ฉันพยายามคิดหาวิธีที่จะหากล่อง bounding ที่พอดีกับสตริงที่ประกอบเพื่อให้วงแหวนโฟกัสเหมือนการแก้ไขไฟล์ใน Finder ทำ ทุกสิ่งที่ฉันลองทำล้มเหลวเมื่อมีช่องว่างที่ท้ายสตริงหรือช่องว่างหลายช่องภายในสตริง ล้มเหลวอย่างน่าสังเวชนี้เช่นเดียวกับboundingRectWithSizeCTFramesetterCreateWithAttributedString

ใช้NSLayoutManagerรหัสต่อไปนี้ดูเหมือนว่าจะทำเคล็ดลับในทุกกรณีที่ฉันได้พบจนถึงและส่งกลับ rect ที่สมบูรณ์ขอบเขตสตริง โบนัส: ถ้าคุณเลือกข้อความขอบของสิ่งที่เลือกไปจนถึงขอบเขตของ rect ที่ส่งคืน รหัสดังต่อการนำมาใช้ประโยชน์ layoutManager NSTextViewจาก

NSLayoutManager* layout = [self layoutManager];
NSTextContainer* container = [self textContainer];

CGRect focusRingFrame = [layout boundingRectForGlyphRange:NSMakeRange(0, [[self textStorage] length]) inTextContainer:container];

2
textView.textContainerInset = UIEdgeInsetsZero;
NSString *string = @"Some string";
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:12.0f], NSForegroundColorAttributeName:[UIColor blackColor]};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[textView setAttributedText:attributedString];
CGRect textViewFrame = [textView.attributedText boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.view.frame)-8.0f, 9999.0f) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];
NSLog(@"%f", ceilf(textViewFrame.size.height));

ทำงานกับแบบอักษรทั้งหมดได้อย่างสมบูรณ์แบบ!


1

ฉันมีปัญหาเดียวกัน แต่ฉันจำได้ว่าตั้งค่าความสูงได้อย่างถูกต้อง ดังนั้นฉันทำต่อไปนี้:

-(CGSize)MaxHeighForTextInRow:(NSString *)RowText width:(float)UITextviewWidth {

    CGSize constrainedSize = CGSizeMake(UITextviewWidth, CGFLOAT_MAX);

    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [UIFont fontWithName:@"HelveticaNeue" size:11.0], NSFontAttributeName,
                                          nil];

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:RowText attributes:attributesDictionary];

    CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];

    if (requiredHeight.size.width > UITextviewWidth) {
        requiredHeight = CGRectMake(0, 0, UITextviewWidth, requiredHeight.size.height);
    }

    return requiredHeight.size;
}

1
    NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [UIFont systemFontOfSize:18], NSFontAttributeName,
                                      [UIColor blackColor], NSForegroundColorAttributeName,
                                      nil];

    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myLabel.text attributes:stringAttributes];
    myLabel.attributedText = attributedString; //this is the key!

    CGSize maximumLabelSize = CGSizeMake (screenRect.size.width - 40, CGFLOAT_MAX);

    CGRect newRect = [myLabel.text boundingRectWithSize:maximumLabelSize
                                                       options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                    attributes:stringAttributes context:nil];

    self.myLabelHeightConstraint.constant = ceilf(newRect.size.height);

ฉันลองทุกอย่างในหน้านี้แล้วและยังมีกรณีหนึ่งสำหรับ UILabel ที่ฟอร์แมตไม่ถูกต้อง จริง ๆ แล้วการตั้งค่าแอคเซทเท็กซ์ Text บนฉลากในที่สุดก็แก้ไขปัญหาได้


1

สิ่งหนึ่งที่ฉันสังเกตเห็นคือ rect ที่จะกลับมาจาก(CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)contextนั้นจะมีความกว้างมากกว่าที่ฉันผ่านเมื่อเกิดเหตุการณ์นี้สตริงของฉันจะถูกตัดทอน ฉันแก้ไขมันเช่นนี้:

NSString *aLongString = ...
NSInteger width = //some width;            
UIFont *font = //your font;
CGRect rect = [aLongString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
                                        options:(NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin)
                                     attributes:@{ NSFontAttributeName : font,
                                                   NSForegroundColorAttributeName : [UIColor whiteColor]}
                                        context:nil];

if(rect.size.width > width)
{
    return rect.size.height + font.lineHeight;
}
return rect.size.height;

สำหรับบริบทเพิ่มเติม ฉันมีข้อความหลายบรรทัดและฉันพยายามค้นหาความสูงที่เหมาะสมเพื่อแสดงมัน boundRectWithSize บางครั้งก็กลับความกว้างที่ใหญ่กว่าที่ฉันจะระบุดังนั้นเมื่อฉันใช้ความกว้างที่ผ่านมาและความสูงที่คำนวณได้เพื่อแสดงข้อความของฉัน จะตัดทอน จากการทดสอบเมื่อ boundingRectWithSize ใช้ความกว้างผิดจำนวนที่จะทำให้ความสูงสั้นโดยเป็น 1 บรรทัด ดังนั้นฉันจะตรวจสอบว่าความกว้างนั้นใหญ่กว่านี้หรือไม่และถ้าเพิ่ม lineHeight ของแบบอักษรเพื่อให้มีพื้นที่เพียงพอที่จะหลีกเลี่ยงการตัดทอน


ความสูงของบรรทัดมีผลกับความกว้างอย่างไร
ร้อยเอ็ด Mulia

@RoiMulia ความสูงของบรรทัดไม่ส่งผลกระทบต่อความกว้าง ฉันได้อัปเดตคำตอบเพื่อให้บริบทเพิ่มเติมว่าการแก้ไขข้อบกพร่องของฉันได้อย่างไร
odyth

0
    NSAttributedString *attributedText =[[[NSAttributedString alloc]
                                          initWithString:joyMeComment.content
                                          attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:TextFont]}] autorelease];

    CGRect paragraphRect =
    [attributedText boundingRectWithSize:CGSizeMake(kWith, CGFLOAT_MAX)
                                 options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                 context:nil];
    contentSize = paragraphRect.size;

    contentSize.size.height+=10;
    label.frame=contentSize;

หากเฟรมของป้ายกำกับไม่เพิ่ม 10 วิธีนี้จะไม่ทำงาน! หวังว่านี่จะช่วยคุณได้! โชคกูเกิล


0
Add Following methods in ur code for getting correct size of attribute string 
1.
    - (CGFloat)findHeightForText:(NSAttributedString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font
 {
    UITextView *textView = [[UITextView alloc] init];
    [textView setAttributedText:text];
    [textView setFont:font];
    CGSize size = [textView sizeThatFits:CGSizeMake(widthValue, FLT_MAX)];
    return size.height;

}

2. Call on heightForRowAtIndexPath method
     int h = [self findHeightForText:attrString havingWidth:yourScreenWidth andFont:urFont];

ในกรณีของฉันฉันควรทำการคำนวณบนเธรดพื้นหลังและไม่อนุญาตให้ใช้องค์ประกอบ UI
Lubbo

0

ฉันต้องการเพิ่มความคิดของฉันเนื่องจากฉันมีปัญหาเดียวกัน

ฉันใช้UITextViewเนื่องจากมันมีการจัดตำแหน่งข้อความที่ดีกว่า (จัดชิดขอบซึ่งในเวลานั้นไม่พร้อมใช้งานUILabel) แต่เพื่อ "จำลอง" ไม่ใช่แบบโต้ตอบไม่เลื่อนUILabelไม่ได้ฉันจะปิดการเลื่อนการตีกลับและการโต้ตอบผู้ใช้อย่างสมบูรณ์ .

แน่นอนปัญหาคือข้อความนั้นเป็นแบบไดนามิกและในขณะที่ความกว้างจะได้รับการแก้ไขความสูงควรถูกคำนวณใหม่ทุกครั้งที่ฉันตั้งค่าข้อความใหม่

boundingRectWithSizeไม่ได้ผลสำหรับฉันเลยจากสิ่งที่ฉันเห็นUITextViewได้เพิ่มขอบด้านบนซึ่งboundingRectWithSizeไม่สามารถนับได้ดังนั้นความสูงที่ดึงมาจากboundingRectWithSizeขนาดเล็กกว่าที่ควรจะเป็น

เนื่องจากข้อความไม่ได้รับการอัปเดตอย่างรวดเร็วจึงใช้สำหรับข้อมูลบางอย่างที่อาจอัปเดตทุก ๆ 2-3 วินาทีฉันจึงตัดสินใจทำตามวิธีต่อไปนี้:

/* This f is nested in a custom UIView-inherited class that is built using xib file */
-(void) setTextAndAutoSize:(NSString*)text inTextView:(UITextView*)tv
{
    CGFloat msgWidth = tv.frame.size.width; // get target's width

    // Make "test" UITextView to calculate correct size
    UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, msgWidth, 300)]; // we set some height, really doesn't matter, just put some value like this one.
    // Set all font and text related parameters to be exact as the ones in targeted text view
    [temp setFont:tv.font];
    [temp setTextAlignment:tv.textAlignment];
    [temp setTextColor:tv.textColor];
    [temp setText:text];

    // Ask for size that fits :P
    CGSize tv_size = [temp sizeThatFits:CGSizeMake(msgWidth, 300)];

    // kill this "test" UITextView, it's purpose is over
    [temp release];
    temp = nil;

    // apply calculated size. if calcualted width differs, I choose to ignore it anyway and use only height because I want to have width absolutely fixed to designed value
    tv.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, msgWidth, tv_size.height );
}

* รหัสข้างต้นไม่ได้ถูกคัดลอกโดยตรงจากแหล่งที่มาของฉันฉันต้องปรับ / ล้างมันจากสิ่งอื่น ๆ ที่ไม่จำเป็นสำหรับบทความนี้ อย่านำไปใช้สำหรับการคัดลอกวางและมันจะทำงานรหัส

ข้อเสียที่เห็นได้ชัดคือมันมีการจัดสรรและปล่อยสำหรับการโทรแต่ละครั้ง

แต่ข้อดีคือคุณหลีกเลี่ยงขึ้นอยู่กับความเข้ากันได้ระหว่างวิธีการ boundingRectWithSize วาดข้อความและคำนวณขนาดและการใช้งานการวาดข้อความในUITextView(หรือUILabelที่คุณสามารถใช้แทนที่UITextViewด้วยUILabel) "บั๊ก" ใด ๆ ที่ Apple อาจหลีกเลี่ยง

ป.ล. ดูเหมือนว่าคุณไม่จำเป็นต้องใช้ "temp" นี้UITextViewและสามารถขอsizeThatFitsโดยตรงจากเป้าหมายได้ แต่นั่นก็ไม่ได้ผลสำหรับฉัน แม้ว่าตรรกะจะบอกว่ามันควรจะทำงานได้และUITextViewไม่จำเป็นต้องจัดสรร / ปล่อยชั่วคราวแต่ก็ไม่ได้ แต่วิธีนี้ใช้ได้อย่างไร้ที่ติสำหรับข้อความใด ๆ ที่ฉันตั้งไว้


ในกรณีของฉันฉันควรทำการคำนวณบนเธรดพื้นหลังและไม่อนุญาตให้ใช้องค์ประกอบ UI
Lubbo

0

ตกลงดังนั้นฉันใช้เวลามากในการแก้ไขข้อบกพร่องนี้ ฉันพบว่าความสูงของข้อความสูงสุดตามที่กำหนดโดยboundingRectWithSizeอนุญาตให้แสดงข้อความโดยของฉันUITextViewต่ำกว่าขนาดเฟรม

ในกรณีของฉันเฟรมมีขนาดสูงสุด 140pt แต่ UITextView สามารถรองรับข้อความได้สูงสุด 131pt

ฉันต้องคิดออกเองและฮาร์ดโค้ดความสูงสูงสุด "ของจริง"

นี่คือทางออกของฉัน:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSString *proposedText = [textView.text stringByReplacingCharactersInRange:range withString:text];
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:proposedText];
    CGRect boundingRect;
    CGFloat maxFontSize = 100;
    CGFloat minFontSize = 30;
    CGFloat fontSize = maxFontSize + 1;
    BOOL fit;
    NSLog(@"Trying text: \"%@\"", proposedText);
    do {
        fontSize -= 1;
        //XXX Seems like trailing whitespaces count for 0. find a workaround
        [attributedText addAttribute:NSFontAttributeName value:[textView.font fontWithSize:fontSize] range:NSMakeRange(0, attributedText.length)];
        CGFloat padding = textView.textContainer.lineFragmentPadding;
        CGSize boundingSize = CGSizeMake(textView.frame.size.width - padding * 2, CGFLOAT_MAX);
        boundingRect = [attributedText boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil];
        NSLog(@"bounding rect for font %f is %@; (max is %f %f). Padding: %f", fontSize, NSStringFromCGRect(boundingRect), textView.frame.size.width, 148.0, padding);
        fit =  boundingRect.size.height <= 131;
    } while (!fit && fontSize > minFontSize);
    if (fit) {
        self.textView.font = [self.textView.font fontWithSize:fontSize];
        NSLog(@"Fit!");
    } else {
        NSLog(@"No fit");
    }
    return fit;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.