Использование cornerRadius в UIImageView в UITableViewCell - программирование

Использование cornerRadius в UIImageView в UITableViewCell

Я использую UIImageView для каждого из моих UITableViewCells, как миниатюры. Мой код использует SDWebImage для асинхронного захвата этих изображений из моего бэкэнд и загрузки их, а затем их кеширования. Это нормально работает.

My UIImageView - это квадрат 50x50, созданный в Interface Builder. Его фон непрозрачен, белый цвет (такой же, как мой цвет фона UITableViewCell, для производительности). Тем не менее, я бы хотел использовать гладкие углы для лучшей эстетики. Если я это сделаю:

UIImageView *itemImageView = (UIImageView *)[cell viewWithTag:100];
    itemImageView.layer.cornerRadius = 2.5f;
    itemImageView.layer.masksToBounds = NO;
    itemImageView.clipsToBounds = YES;

Мой TableView падает около 5-6 кадров сразу, укупорки около 56 кадров в секунду во время быстрой прокрутки (что хорошо), но когда я перетащить и тянуть контроль обновления, она отстает немного и падает до примерно 40 кадров в секунду. Если я удалю строку cornerRadius, все будет нормально и не будет отставать. Это было протестировано на iPod touch 5G с помощью инструментов.

Есть ли другой способ, которым я мог бы иметь округленные UIImageView для моих ячеек и не пострадать от удара производительности? Я уже оптимизировал свой cellForRowAtIndexPath, и я получаю 56-59 FPS при быстрой прокрутке без cornerRadius.

4b9b3361

Ответ 1

Да, это потому, что cornerRadius и clipToBounds требуют экранированного рендеринга, я предлагаю вам прочитать этот ответ с одного из моих question. Я также цитирую две сессии WWDC, которые вы должны увидеть.
Лучшее, что вы можете сделать, это захватить изображение сразу после загрузки, а в другом потоке отправить метод, который вокруг изображений. Желательно, чтобы вы работали над изображением вместо изображения.

// Get your image somehow
UIImage *image = [UIImage imageNamed:@"image.jpg"];

// Begin a new image that will be the new image with the rounded corners 
// (here with the size of an UIImageView)
 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);

 // Add a clip before drawing anything, in the shape of an rounded rect
  [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds 
                        cornerRadius:10.0] addClip];
 // Draw your image
[image drawInRect:imageView.bounds];

 // Get the image, here setting the UIImageView image
  imageView.image = UIGraphicsGetImageFromCurrentImageContext();

 // Lets forget about that we were drawing
  UIGraphicsEndImageContext();

Метод grabbed здесь Вы также можете подклассифицировать tableviewcell и переопределить метод drawRect.
Грязный, но очень эффективный способ - нарисовать маску в фотошопе с внутренней альфой и вокруг соответствующего цвета фона ячейки и добавить еще одно изображение, не непрозрачное с четким цветом фона, на изображение с изображениями.

Ответ 2

Неблокирующее решение

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

+ (void)roundedImage:(UIImage *)image
          completion:(void (^)(UIImage *image))completion {
    dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Begin a new image that will be the new image with the rounded corners
        // (here with the size of an UIImageView)
        UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
        CGRect rect = CGRectMake(0, 0, image.size.width,image.size.height);

        // Add a clip before drawing anything, in the shape of an rounded rect
        [[UIBezierPath bezierPathWithRoundedRect:rect
                                    cornerRadius:image.size.width/2] addClip];
        // Draw your image
        [image drawInRect:rect];

        // Get the image, here setting the UIImageView image
        UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();

        // Lets forget about that we were drawing
        UIGraphicsEndImageContext();
        dispatch_async( dispatch_get_main_queue(), ^{
            if (completion) {
                completion(roundedImage);
            }
        });
    });
}

Ответ 3

Для этого есть еще одно хорошее решение. Я делал это несколько раз в своих проектах. Если вы хотите создать закругленные углы или что-то еще, вы можете просто использовать обложку перед вашим основным изображением.

Например, вы хотите сделать закругленные углы. В этом случае вам нужен квадратный слой изображения с вырезанной окружностью в центре.

Используя этот метод, вы получите 60 кадров в секунду при прокрутке внутри UITableView или UICollectionView. Поскольку этот метод не требуется для рендеринга экрана для настройки UIImageView (например, аватары и т.д.).

Ответ 4

Свифт 3 версии ответа thedeveloper3124

func roundedImage(image: UIImage, completion: @escaping ((UIImage?)->(Void))) {
    DispatchQueue.global().async {
        // Begin a new image that will be the new image with the rounded corners
        // (here with the size of an UIImageView)
        UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
        let rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

        // Add a clip before drawing anything, in the shape of an rounded rect
        UIBezierPath(roundedRect: rect, cornerRadius: image.size.width/2).addClip()

        // Draw your image
        image.draw(in: rect)

        // Get the image, here setting the UIImageView image
        guard let roundedImage = UIGraphicsGetImageFromCurrentImageContext() else {
            print("UIGraphicsGetImageFromCurrentImageContext failed")
            completion(nil)
            return
        }

        // Lets forget about that we were drawing
        UIGraphicsEndImageContext()

        DispatchQueue.main.async {
            completion(roundedImage)
        }
    }
}