Zoom UILabel и повторно отобразить шрифт правильного размера

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

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

Я справлюсь с этим методом:

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

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

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

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

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


person Ben Scheirman    schedule 02.01.2010    source источник


Ответы (8)


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

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

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

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

person Brad Larson    schedule 02.01.2010
comment
Спасибо за подробный ответ. Означает ли это, что мне нужно отказаться от встроенной функции масштабирования в режиме прокрутки? Или, когда я закончу масштабирование, я просто повторно визуализирую поверхность с новым масштабным коэффициентом? Как это влияет на настройки минимального / максимального масштабирования? - person Ben Scheirman; 02.01.2010
comment
Вы используете встроенное масштабирование во время выполнения жеста сжатия, а затем повторно выполняете рендеринг, когда жест завершен. Это единственный способ получить приличную производительность масштабирования щипком. UIScrollView отслеживает общий масштаб масштабирования (вот почему вам нужно переопределить средство доступа преобразования в представлении содержимого, чтобы противодействовать этому), поэтому он по-прежнему будет соблюдать настройки минимального / максимального масштабирования. - person Brad Larson; 03.01.2010
comment
В поисках смысла. Поэтому, если я увеличиваю масштаб до 150%, я могу изменить размер шрифта моей метки на 1,5 * currentFontSize, пересчитать их точное положение прокрутки и повторно отобразить мою метку. Затем я сбрасываю масштаб до 1 и пересчитываю мин / макс? Я имею это право? - person Ben Scheirman; 04.01.2010
comment
Это потрясающе хороший материал, и, видимо, вы единственный парень, который что-то опубликовал по этому поводу (здесь, на SO :)) - person Dan Rosenstark; 23.07.2010
comment
@Yar - Рад, что это было полезно. Возможно, вы захотите проверить обновление, которое я добавил к связанному ответу, потому что некоторые из этих действий масштабирования изменились в iPhone OS 3.2+. - person Brad Larson; 23.07.2010
comment
Привет, может быть, немного поздно, но я попытался изменить размер шрифта, но он все еще выглядел размытым. Любая идея? Я использую текстовое представление. - person user281300; 03.11.2011
comment
@ user281300 - Вы уверены, что изменили размер шрифта и не применили какое-либо преобразование масштабирования к представлению? Если да, убедитесь, что исходная точка представления все еще выровнена по границе целого пикселя. Один из способов проверить это - использовать инструмент Core Animation, который позволяет раскрашивать несовпадающие слои. - person Brad Larson; 03.11.2011
comment
хорошо спасибо. Е. Я попытался изменить размер шрифта, и это выглядело не очень хорошо. Преобразования нет. Просто используйте масштаб по умолчанию, а затем измените размер. - person user281300; 07.11.2011

Спасибо 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));
     }];
person JEzu    schedule 15.06.2012
comment
Что случилось с последней строчкой? CGAffineTransformMakeRotation (1) вращает метку примерно на 1 радиан. - person Diana Farin; 01.08.2017

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 только для ваших ярлыков.

person kabucey    schedule 02.11.2012
comment
идеально! это самое простое решение - person coolcool1994; 06.07.2015
comment
У меня не получилось. После масштабирования этикетка остается размытой. - person Diana Farin; 01.08.2017

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

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))
}

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

person Rafal    schedule 04.12.2015
comment
Это мой предпочтительный способ решения проблемы, однако вам не нужно преобразование аффинного вращения (CGAffineTransform(rotationAngle: 0)). - person Wizard; 23.01.2019

Я реализовал решение Scrimmers, создав подкласс UILabel как DetailUILabel с такими методами переопределения;

импорт 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;
}

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

+ (Class)layerClass {
    return [CATiledLayer class];
}
person Eralp Karaduman    schedule 02.01.2014

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

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

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

person valvoline    schedule 11.07.2011

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

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

person BillChan    schedule 30.08.2012

Есть более простой способ сделать это ..

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

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

Работа выполнена

person Scrimmers    schedule 17.03.2011