Увеличить шрифт UILabel & Re-render в правильном размере

У меня есть многострочный UILabel, который я хочу включить масштабирование.

Я вложил его с помощью UIScrollView и установил минимальное масштабирование до 0,25 и максимальное увеличение до 4. Это хорошо работает, однако мой шрифт UILabel выглядит довольно грубым при любом уровне масштабирования, отличном от 1.

Я могу обработать этот метод:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale

чтобы изменить размер шрифта моего UILabel на нечто большее, но вид все еще увеличивается, поэтому он всегда выглядит ужасно.

Есть ли способ сделать повторный рендеринг текста ярлыка? Я сделал масштабирование?

Важно, чтобы текущая прокрученная позиция в тексте не терялась.

(Чтобы понять, что я собираюсь, обратите внимание на то, как в Mobile Safari при масштабировании текст масштабируется/сглаживается на долю секунды, затем он очищается, чтобы хорошо отображать текущую масштаб масштаба)

Ответ 1

Встроенное масштабирование UIScrollView применяет только преобразование к вашему представлению контента, что приводит к размытости при чем-либо, превышающем масштабный коэффициент 1,0. Для действительно четкого рендеринга вам потребуется обработать масштабирование самостоятельно. Я описываю фрагмент процесса в этом ответе.

Вам нужно будет отслеживать масштабный коэффициент представления содержимого вручную, а затем в методе -scrollViewDidEndZooming: withView: atScale: delegate вы примените эту шкалу. Для вашего UILabel это будет означать изменение размера шрифта, чтобы отразить новый масштаб.

Чтобы сохранить правильную позицию прокрутки, вам нужно захватить contentOffset для UIScrollView в указанном выше методе делегата и рассчитать, какая позиция соответствует обновленному UILabel. Затем вы устанавливаете contentSize прокрутки в соответствии с новым размером UILabel и используете -setContentOffset: animated: для установки вновь вычисленного смещения контента (с анимированным набором в НЕТ).

Немного сложно получить математику правильно, но я делаю это при масштабировании текста в одном из моих приложений, что можно увидеть в демонстрации этой заявки (примерно на отметке 1/3).

Ответ 2

Спасибо valvoline и Scrimmers за ваши ответы. Мое решение было комбинацией между вашими.

Подкласс UILabel выглядит следующим образом:

UILabelZoomable.h

+ (Class) layerClass;

UILabelZoomable.m

+ (Class) layerClass
{    
    return [CATiledLayer class];
}

Теперь, когда вы используете свой новый UILabelZoomable в своем приложении, не забудьте сделать это:

YourViewController.m

CATiledLayer *tiledLayer = (CATiledLayer*)textoLabel.layer;
tiledLayer.levelsOfDetail = 10;
tiledLayer.levelsOfDetailBias = 10;

Не забудьте добавить структуру QuartzCore!

#import <QuartzCore/QuartzCore.h>

Наслаждайтесь резким и красивым визуализированным текстом:

[UIView animateWithDuration:1 animations:^
     {
         yourLabel.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(200, 200), CGAffineTransformMakeRotation(1));
     }];

Ответ 3

iOS 4 +

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    scrollView.contentScaleFactor = scale;
    for (UIView *subview in scrollView.subviews) {
        subview.contentScaleFactor = scale;
    }
}

Если это становится слишком интенсивным, а производительность медленная, просто попробуйте установить contentScaleFactor только для своих меток.

Ответ 4

Я реализовал решение Scrimmers, выполнив подклассификацию UILabel как DetailedUILabel с помощью таких переопределяющих методов:

import QuartzCore

#import <QuartzCore/QuartzCore.h>

переопределить метод init, initWithFrame в зависимости от того, что вы хотите.

- (id)init
{
    self = [super init];
    if (self) {
        CATiledLayer *tiledLayer = (CATiledLayer *)self.layer;
        tiledLayer.levelsOfDetailBias = 4;
        tiledLayer.levelsOfDetail = 4;
        self.opaque = YES;
    }
    return self;
}

и classClass, метод класса.

+ (Class)layerClass {
    return [CATiledLayer class];
}

Ответ 5

Правильный способ заменить CALayer на CATiledLayer выглядит следующим образом:

+ (Class)layerClass {
    return [CATiledLayer class]; 
}

вы должны установить свою детализацию и все, что вам нужно.

Ответ 6

Я тестировал код JEzu в iOS 5.1 и 6 beta, он работает для текстовой метки const, но причиной того, что другая нормальная метка требует обновления текстового содержимого, не изменяется, т.е. текст не изменяется...

Я подклассифицирую UILabel как ZoomableLabel с ответом JEzu и применяю класс как пользовательский класс метки, и он отлично работает.

Ответ 7

Вот что я придумал:

func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) {
        label.font = UIFont(name: "YourFont", size: fontSize * scale)
        label.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(1 / scale, 1 / scale), CGAffineTransformMakeRotation(0))
    }

Обновлено для Swift 4.0:

func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
    label.font = label.font.withSize(fontSize * scale)
    label.transform = CGAffineTransform(scaleX: 1 / scale, y: 1 / scale).concatenating(CGAffineTransform(rotationAngle: 0))
}

А также помните, что ярлык должен быть достаточно большим, чтобы отобразить весь текст. Иногда требуется перерисовать крошечный бит, но этот метод довольно прост.

Ответ 8

Существует более простой способ сделать это.

Замените layerClass UILabel на CATiledLayer и соответствующим образом установите уровень детализации.

+ (Class)layerClass
{
    CATiledLayer *layerForView = (CATiledLayer *)self.layer;
    layerForView.levelsOfDetailBias = 2;
    layerForView.levelsOfDetail = 2;
    return [CATiledLayer class];
}

Задание выполнено