Ответ 1
Эта проблема - реальная задача PITA. Это не помогает, что API, который работает, устарел в iOS7 или что API замены iOS7 нарушен. Мля!
Ваше решение хорошо, однако оно использует устаревший API (sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:
), и он не очень хорошо инкапсулирован - вам нужно скопировать этот код в любые ячейки или представления, где вы хотите этого поведения. С положительной стороны это довольно эффективно! Одна ошибка может заключаться в том, что метка еще не была выложена, когда вы выполняете расчет, но вы выполняете расчет на основе его ширины.
Я предлагаю вам инкапсулировать это поведение в подкласс UILabel. Поместив вычисление размера в переопределенном методе intrinsicContentSize
, метка будет автоматически настраиваться. Я написал следующее, которое включает ваш код, который будет выполняться на iOS6, и мою версию, используя не устаревший API для iOS7 или лучше:
@implementation TSAutoHeightLabel
- (CGSize) intrinsicContentSize
{
NSAssert( self.baselineAdjustment == UIBaselineAdjustmentAlignCenters, @"Please ensure you are using UIBaselineAdjustmentAlignCenters!" );
NSAssert( self.numberOfLines == 1, @"This is only for single-line labels!" );
CGSize intrinsicContentSize;
if ( [self.text respondsToSelector: @selector( boundingRectWithSize:options:attributes:context: )] )
{
NSStringDrawingContext* context = [NSStringDrawingContext new];
context.minimumScaleFactor = self.minimumScaleFactor;
CGSize inaccurateSize = [self.text boundingRectWithSize: CGSizeMake( self.bounds.size.width, CGFLOAT_MAX )
options: NSStringDrawingUsesLineFragmentOrigin
attributes: @{ NSFontAttributeName : self.font }
context: context].size;
CGSize accurateSize = [self.text sizeWithAttributes: @{ NSFontAttributeName : [UIFont fontWithName: self.font.fontName size: 12.0] } ];
CGFloat accurateHeight = accurateSize.height * inaccurateSize.width / accurateSize.width;
intrinsicContentSize = CGSizeMake( inaccurateSize.width, accurateHeight);
}
else
{
CGFloat actualFontSize;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
[self.text sizeWithFont: self.font
minFontSize: self.minimumFontSize
actualFontSize: &actualFontSize
forWidth: self.frame.size.width
lineBreakMode: NSLineBreakByTruncatingTail];
#pragma GCC diagnostic pop
CGRect lineBox = CTFontGetBoundingBox((__bridge CTFontRef)([UIFont fontWithName: self.font.fontName size: actualFontSize]));
intrinsicContentSize = lineBox.size;
}
return intrinsicContentSize;
}
@end
Эта реализация не идеальна. Я должен был обеспечить использование baselineAdjustment == UIBaselineAdjustmentAlignCenters, и я не уверен на 100%, я понимаю, почему. И я не доволен обручами, которые мне пришлось перепрыгнуть, чтобы получить точную высоту текста. Там также разница в пикселях между тем, что производит мой расчет, и вашим. Не стесняйтесь играть с ним и при необходимости настраивайте:)
API boundingRectWithSize:options:attributes:context
кажется мне сломанным. Хотя он (в основном!) Правильно сжимает текст до размера ввода, он не вычисляет правильную высоту! Высота, которую он возвращает, основана на высоте строки поставляемого шрифта, даже если масштабирование находится в игре. Мое предположение заключается в том, почему UILabel
не имеет такого поведения по умолчанию? Моим обходным путем является вычисление неограниченного размера, где высота и ширина являются точными, а затем используйте соотношение между ограниченной и неограниченной шириной для вычисления точной высоты для ограниченного размера. Что такое ПИТА. На форумах Apple dev есть много жалоб и здесь, на SO, которые указывают, что этот API имеет ряд проблем, подобных этому.