Как обрезать UIImageView до Нового UIImage в режиме' aspect fit'?


Введите описание изображения здесь

Я хотел бы создать новый UIImage, обрезав изображение внутри UIImageView. Например, на приведенном выше рисунке я хочу получить новое изображение области, выделенной зеленым цветом. Я нашел код для этого, обычно он выглядит примерно так (как категория UIImage):

- (UIImage *)croppedImage:(CGRect)bounds {
    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], bounds);
    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    return croppedImage;
}

Имеет смысл. Тем не менее, мой UIImageView находится в contentMode 'aspect fit'. Похоже, что это усложняет вычисления CGRect для CGImageCreateWithImageInRect, и я не смог выяснить правильное решение.

Я полагаю, что мне нужно применить те же преобразования к зеленой прямой кишке, что и к изображению?

Также: я нашел здесь этот другой вопрос ( Как узнать размер изображения после применения аспекта, подходящего для изображения в UIImageView), который, кажется, показывает грубый способ получения размера, но я все еще не уверен, как это вписывается в ситуацию обрезки.

Есть идеи?

EDIT : в моем случае у меня буквально есть квадрат, как показано выше, нарисованное в виде наложения поверх UIImageView. Поэтому, хотя я понимаю, что обрезаю UIImage (внутри UIImageView), мне нужно каким-то образом изменить зеленый CGRect таким образом, чтобы он "соответствовал" преобразованию, выполненному при применении "подгонки аспекта". Например, если я оставляю свой UIImageView в "верхнем левом" режиме, то все работает нормально, потому что существует сопоставление 1:1 между базовым UIImage и UIImageView. Проблема в режиме "сверху слева" заключается в том, что не всегда все изображение дисплей, потому что он может быть больше, чем мой UIImageView.

4 8

4 ответа:

Основываясь на моем другом ответе на аналогичный вопрос, я написал этот метод для категории UIImageView:

-(CGRect) cropRectForFrame:(CGRect)frame
{
    NSAssert(self.contentMode == UIViewContentModeScaleAspectFit, @"content mode must be aspect fit");

    CGFloat widthScale = self.bounds.size.width / self.image.size.width;
    CGFloat heightScale = self.bounds.size.height / self.image.size.height;

    float x, y, w, h, offset;
    if (widthScale<heightScale) {
        offset = (self.bounds.size.height - (self.image.size.height*widthScale))/2;
        x = frame.origin.x / widthScale;
        y = (frame.origin.y-offset) / widthScale;
        w = frame.size.width / widthScale;
        h = frame.size.height / widthScale;
    } else {
        offset = (self.bounds.size.width - (self.image.size.width*heightScale))/2;
        x = (frame.origin.x-offset) / heightScale;
        y = frame.origin.y / heightScale;
        w = frame.size.width / heightScale;
        h = frame.size.height / heightScale;
    }
    return CGRectMake(x, y, w, h);
}

Вы можете пройти в кадр вашего зеленого rect (предполагая, что это подвид изображения) и получить кадрирование rect обратно для обрезки UIImage. Обратите внимание, что он работает только для режима "aspect fit"! Но я его не проверял. Так скажи мне, если это сработает!

Я знаю, что ответ был, но у меня были некоторые проблемы с получением выше прямо так, я обычно публикую свое решение тоже.

Проблема заключается в том, что изображение масштабируется с учетом аспекта, но мы знаем масштабный коэффициент как для ширины, так и для высоты. Тогда мы можем вычислить правильный прямоугольник обрезки довольно легко:
-(UIImage*)crop:(CGRect)frame
{
    // Find the scalefactors  UIImageView's widht and height / UIImage width and height
    CGFloat widthScale = self.bounds.size.width / self.image.size.width;
    CGFloat heightScale = self.bounds.size.height / self.image.size.height;

    // Calculate the right crop rectangle 
    frame.origin.x = frame.origin.x * (1 / widthScale);
    frame.origin.y = frame.origin.y * (1 / heightScale);
    frame.size.width = frame.size.width * (1 / widthScale);
    frame.size.height = frame.size.height * (1 / heightScale);

    // Create a new UIImage
    CGImageRef imageRef = CGImageCreateWithImageInRect(self.image.CGImage, frame);
    UIImage *croppedImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    return croppedImage;
}

Я думаю, что вы не понимаете, что вы делаете. Вы не можете "обрезать" UIImageView-только UIImages. Поэтому ваш метод croppedImage должен быть в категории UIImage, а не в категории UIImageView. Границы rect, которые вы передаете методу crop, должны быть связаны с размером изображения, а не с границами imageview. Не имеет значения, что такое contentMode представления изображения - обрезка изображения всегда будет работать одинаково. Сначала получите изображение из представления изображения. Затем создайте новое обрезанное изображение из него и, наконец, установить обрезанное изображение для просмотра изображения. Это можно сделать в одной строке кода:

imageView.image = [imageView.image croppedImage:rect];

Так как ваш contentMode - это 'aspect fit', новое изображение будет автоматически растягиваться, чтобы поместиться в рамке вида. Вы также можете попробовать изменить рамку вида на основе изображения.размер, чтобы избежать эффектов пикселизации.

Я использую следующий метод.

-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
   CGSize cropSize = rect.size;
   CGFloat widthScale =  
   image.size.width/self.imageViewOriginal.bounds.size.width;
   CGFloat heightScale = 
   image.size.height/self.imageViewOriginal.bounds.size.height;
   cropSize = CGSizeMake(rect.size.width*widthScale,  
   rect.size.height*heightScale);
   CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
   rect.origin.y*heightScale);
   rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
   cropSize.height);
   CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
   UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
   CGImageRelease(subImage);
   return croppedImage;

   }