[objective-c] 잘못된 크기를 반환하는 NSAttributedString에 대한 boundingRectWithSize

기여한 문자열에 대한 rect를 얻으려고하지만 boundingRectWithSize 호출은 전달하는 크기를 고려하지 않고 큰 높이 (긴 문자열)가 아닌 단일 행 높이의 rect를 반환합니다. 아래 코드와 같이 높이와 0에 대해 매우 큰 값을 전달하여 실험했지만 반환 된 rect는 항상 동일합니다.

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

이 문제가 해결 되었습니까? 아니면 줄 바꿈 된 텍스트에 대한 rect를 반환하려면 다른 작업을 수행해야합니까?



답변

올바른 옵션을 제공하지 않은 것 같습니다. 라벨 포장의 경우 최소한 다음을 제공하십시오.

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

참고 : 원본 텍스트 너비가 300.f 미만인 경우 줄 바꿈이 없으므로 경계 크기가 올바른지 확인하십시오. 그렇지 않으면 여전히 잘못된 결과가 표시됩니다.


답변

어떤 이유로 든 boundingRectWithSize는 항상 잘못된 크기를 반환합니다. 나는 해결책을 찾았다. UItextView -sizeThatFits에는 텍스트 세트에 적합한 크기를 리턴하는 메소드가 있습니다. 따라서 boundingRectWithSize를 사용하는 대신 임의의 프레임으로 UITextView를 작성하고 각각의 너비와 CGFLOAT_MAX 높이로 sizeThatFits를 호출하십시오. 적절한 높이를 가진 크기를 반환합니다.

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

while 루프에서 크기를 계산하는 경우 자동 릴리스 풀에 크기를 추가하는 것을 잊지 마십시오. n 개의 UITextView가 생성되므로 autoreleasepool을 사용하지 않으면 앱의 런타임 메모리가 증가합니다.


답변

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 는 올바른 높이를 가지지 않을 것입니다. 알 anotherString은 (추가 된 캐릭터 )의 속성 사전없이 초기화. 이것은 anotherString 의 적법한 초기화 프로그램 이지만 boundingRectWithSize : 는 정확한 크기를 제공하지 않습니다.


답변

오랜 조사 후의 최종 결정 :
- boundingRectWithSize함수는 중단되지 않은 일련의 문자에 대해서만 올바른 크기를 반환합니다! 문자열에 공백이나 다른 것이 포함되어있는 경우 (Apple “글리프 중 일부”라고 함) 텍스트를 표시하는 데 필요한 rect의 실제 크기를 얻는 것은 불가능합니다!
문자열의 공백을 문자로 바꾸고 즉시 올바른 결과를 얻었습니다.

애플은 여기에 말한다 :
https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

“이 메서드는 문자열에서 글리프의 실제 경계를 반환합니다. 일부 글리프 (예 : 공백)는 전달 된 크기로 지정된 레이아웃 제약 조건과 겹칠 수 있으므로 일부 경우 크기 구성 요소의 너비 값은 반환 CGRect된 크기 매개 변수의 너비 값을 초과 할 수 있습니다. “

따라서 실제 rect를 계산하는 다른 방법을 찾아야합니다.


오랜 조사 끝에 해결책이 마침내 발견되었습니다 !!! 나는 그것이 관련된 모든 경우에 잘 작동하는지 확신 할 수 UITextView없지만 주요하고 중요한 것이 감지되었습니다!

boundingRectWithSize함수뿐만 아니라 CTFramesetterSuggestFrameSizeWithConstraints(및 다른 많은 방법)는 올바른 사각형을 사용할 때 크기와 텍스트 부분을 정확하게 계산합니다. 예를 들어 -has-및이 값 UITextViewtextView.bounds.size.width텍스트를 그릴 때 시스템에서 사용되는 실제 사각형이 아닙니다 UITextView.

매우 흥미로운 매개 변수를 발견하고 코드에서 간단한 계산을 수행했습니다.

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

그리고 마술 작품-내 모든 텍스트가 올바르게 계산되었습니다! 즐겨!


답변

스위프트 4 버전

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를 사용하여 텍스트를 측정하면 정수 크기를 제공하고 이모티콘 및 기타 유니 코드 문자를 잘 처리하므로 가장 효과적입니다.


답변

나는 이러한 제안들 중 어느 것도 운이 없었다. 내 문자열에는 유니 코드 글 머리 기호가 포함되어 계산에서 슬픔을 일으킨 것으로 보입니다. UITextView가 도면을 잘 처리하고 있음을 알았으므로 계산을 활용하기 위해 그것을 찾았습니다. NSString 그리기 방법만큼 최적은 아니지만 다음과 같은 작업을 수행했지만 적어도 정확합니다. 호출하기 위해 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);


답변

꼬리를 잘라서 경계 상자를 얻으려면 이 질문 이 도움 될 수 있습니다.

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