Ich habe seit buchstäblichen Tagen mein Gesicht in dieses hineingeschlagen und obwohl ich ständig das Gefühl habe, am Rande der Offenbarung zu sein, kann ich mein Ziel einfach nicht erreichen.
Ich dachte schon in der Konzeptphase meines Designs, dass es eine triviale Angelegenheit wäre, ein Bild von der Kamera oder Bibliothek des iPhones aufzunehmen, es auf eine bestimmte Höhe zu verkleinern und dabei eine Funktion zu verwenden, die der folgenden entspricht: Aspektfüllung Option von UIImageView (vollständig im Code) und dann Zuschneiden alles, was nicht in ein übergebenes CGRect passte.
Das ursprüngliche Bild von der Kamera oder Bibliothek zu erhalten, war trivial. Ich bin schockiert, wie schwierig sich die beiden anderen Schritte erwiesen haben.
Das beigefügte Bild zeigt, was ich zu erreichen versuche. Wäre jemand bitte so freundlich, meine Hand zu halten? Jedes Codebeispiel, das ich bisher gefunden habe, scheint das Bild zu zerschlagen, verkehrt herum zu stehen, wie Mist auszusehen, außerhalb der Grenzen zu zeichnen oder auf andere Weise einfach nicht richtig zu funktionieren.
Ich brauchte dasselbe - in meinem Fall, um die Bemaßung auszuwählen, die einmal skaliert passt, und dann jedes Ende zuzuschneiden, um den Rest an die Breite anzupassen. (Ich arbeite im Querformat, habe also möglicherweise keine Mängel im Hochformat festgestellt.) Hier ist mein Code - er ist Teil einer Kategorie in UIImage. Die Zielgröße in meinem Code ist immer auf die Vollbildgröße des Geräts eingestellt.
@implementation UIImage (Extras)
#pragma mark -
#pragma mark Scale and crop image
- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
{
UIImage *sourceImage = self;
UIImage *newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO)
{
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor)
{
scaleFactor = widthFactor; // scale to fit height
}
else
{
scaleFactor = heightFactor; // scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor)
{
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
}
else
{
if (widthFactor < heightFactor)
{
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
}
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.Origin = thumbnailPoint;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if(newImage == nil)
{
NSLog(@"could not scale image");
}
//pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
Ein älterer Beitrag enthält Code für eine Methode zum Ändern der Größe Ihres UIImage. Der relevante Teil ist wie folgt:
+ (UIImage*)imageWithImage:(UIImage*)image
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Was das Beschneiden betrifft, sollte das resultierende Bild meines Erachtens an die Grenzen des Kontexts gekürzt werden, wenn Sie die Methode so ändern, dass für die Skalierung eine andere Größe als für den Kontext verwendet wird.
+ (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)targetSize {
//If scaleFactor is not touched, no scaling will occur
CGFloat scaleFactor = 1.0;
//Deciding which factor to use to scale the image (factor = targetSize / imageSize)
if (image.size.width > targetSize.width || image.size.height > targetSize.height)
if (!((scaleFactor = (targetSize.width / image.size.width)) > (targetSize.height / image.size.height))) //scale to fit width, or
scaleFactor = targetSize.height / image.size.height; // scale to fit heigth.
UIGraphicsBeginImageContext(targetSize);
//Creating the rect where the scaled image is drawn in
CGRect rect = CGRectMake((targetSize.width - image.size.width * scaleFactor) / 2,
(targetSize.height - image.size.height * scaleFactor) / 2,
image.size.width * scaleFactor, image.size.height * scaleFactor);
//Draw the image into the rect
[image drawInRect:rect];
//Saving the image, ending image context
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
Ich schlage dieses vor. Ist sie nicht eine Schönheit? ;)
Es gibt einen großartigen Code, der sich auf die Größenänderung von Bildern und einige andere Vorgänge bezieht. Ich habe dieses Problem gelöst, als ich versuchte, herauszufinden, wie die Größe von Bildern geändert werden kann ... http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way /
Bitte schön. Dieser ist perfekt ;-)
BEARBEITEN: siehe unten Kommentar - "Funktioniert nicht mit bestimmten Bildern, schlägt fehl mit: CGContextSetInterpolationQuality: ungültiger Kontext 0x0 Fehler"
// Resizes the image according to the given content mode, taking into account the image's orientation
- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode imageToScale:(UIImage*)imageToScale bounds:(CGSize)bounds interpolationQuality:(CGInterpolationQuality)quality {
//Get the size we want to scale it to
CGFloat horizontalRatio = bounds.width / imageToScale.size.width;
CGFloat verticalRatio = bounds.height / imageToScale.size.height;
CGFloat ratio;
switch (contentMode) {
case UIViewContentModeScaleAspectFill:
ratio = MAX(horizontalRatio, verticalRatio);
break;
case UIViewContentModeScaleAspectFit:
ratio = MIN(horizontalRatio, verticalRatio);
break;
default:
[NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode];
}
//...and here it is
CGSize newSize = CGSizeMake(imageToScale.size.width * ratio, imageToScale.size.height * ratio);
//start scaling it
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
CGImageRef imageRef = imageToScale.CGImage;
CGContextRef bitmap = CGBitmapContextCreate(NULL,
newRect.size.width,
newRect.size.height,
CGImageGetBitsPerComponent(imageRef),
0,
CGImageGetColorSpace(imageRef),
CGImageGetBitmapInfo(imageRef));
CGContextSetInterpolationQuality(bitmap, quality);
// Draw into the context; this scales the image
CGContextDrawImage(bitmap, newRect, imageRef);
// Get the resized image from the context and a UIImage
CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
// Clean up
CGContextRelease(bitmap);
CGImageRelease(newImageRef);
return newImage;
}
Dies ist eine Version der Antwort von Jane Sales in Swift. Prost!
public func resizeImage(image: UIImage, size: CGSize) -> UIImage? {
var returnImage: UIImage?
var scaleFactor: CGFloat = 1.0
var scaledWidth = size.width
var scaledHeight = size.height
var thumbnailPoint = CGPointMake(0, 0)
if !CGSizeEqualToSize(image.size, size) {
let widthFactor = size.width / image.size.width
let heightFactor = size.height / image.size.height
if widthFactor > heightFactor {
scaleFactor = widthFactor
} else {
scaleFactor = heightFactor
}
scaledWidth = image.size.width * scaleFactor
scaledHeight = image.size.height * scaleFactor
if widthFactor > heightFactor {
thumbnailPoint.y = (size.height - scaledHeight) * 0.5
} else if widthFactor < heightFactor {
thumbnailPoint.x = (size.width - scaledWidth) * 0.5
}
}
UIGraphicsBeginImageContextWithOptions(size, true, 0)
var thumbnailRect = CGRectZero
thumbnailRect.Origin = thumbnailPoint
thumbnailRect.size.width = scaledWidth
thumbnailRect.size.height = scaledHeight
image.drawInRect(thumbnailRect)
returnImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return returnImage
}
Ich habe Brad Larsons Code geändert. Es füllt das Bild im angegebenen Rect-Format.
-(UIImage*) scaleAndCropToSize:(CGSize)newSize;
{
float ratio = self.size.width / self.size.height;
UIGraphicsBeginImageContext(newSize);
if (ratio > 1) {
CGFloat newWidth = ratio * newSize.width;
CGFloat newHeight = newSize.height;
CGFloat leftMargin = (newWidth - newHeight) / 2;
[self drawInRect:CGRectMake(-leftMargin, 0, newWidth, newHeight)];
}
else {
CGFloat newWidth = newSize.width;
CGFloat newHeight = newSize.height / ratio;
CGFloat topMargin = (newHeight - newWidth) / 2;
[self drawInRect:CGRectMake(0, -topMargin, newSize.width, newSize.height/ratio)];
}
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Ich konvertierte Sam Wirchs Anleitung zu Swift und es funktionierte gut für mich, obwohl das endgültige Bild ein leichtes "Quetschen" aufweist, das ich nicht auflösen konnte.
func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage {
var ratio: CGFloat = 0
var delta: CGFloat = 0
var offset = CGPointZero
if image.size.width > image.size.height {
ratio = newSize.width / image.size.width
delta = (ratio * image.size.width) - (ratio * image.size.height)
offset = CGPointMake(delta / 2, 0)
} else {
ratio = newSize.width / image.size.height
delta = (ratio * image.size.height) - (ratio * image.size.width)
offset = CGPointMake(0, delta / 2)
}
let clipRect = CGRectMake(-offset.x, -offset.y, (ratio * image.size.width) + delta, (ratio * image.size.height) + delta)
UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
UIRectClip(clipRect)
image.drawInRect(clipRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Wenn jemand die Objective C-Version haben möchte, ist sie auf seiner Website verfügbar.
Die Xamarin.iOS-Version für akzeptierte Antwort zum Ändern der Größe und zum Zuschneiden von UIImage (Aspektfüllung) ist unten
public static UIImage ScaleAndCropImage(UIImage sourceImage, SizeF targetSize)
{
var imageSize = sourceImage.Size;
UIImage newImage = null;
var width = imageSize.Width;
var height = imageSize.Height;
var targetWidth = targetSize.Width;
var targetHeight = targetSize.Height;
var scaleFactor = 0.0f;
var scaledWidth = targetWidth;
var scaledHeight = targetHeight;
var thumbnailPoint = PointF.Empty;
if (imageSize != targetSize)
{
var widthFactor = targetWidth / width;
var heightFactor = targetHeight / height;
if (widthFactor > heightFactor)
{
scaleFactor = widthFactor;// scale to fit height
}
else
{
scaleFactor = heightFactor;// scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor)
{
thumbnailPoint.Y = (targetHeight - scaledHeight) * 0.5f;
}
else
{
if (widthFactor < heightFactor)
{
thumbnailPoint.X = (targetWidth - scaledWidth) * 0.5f;
}
}
}
UIGraphics.BeginImageContextWithOptions(targetSize, false, 0.0f);
var thumbnailRect = new RectangleF(thumbnailPoint, new SizeF(scaledWidth, scaledHeight));
sourceImage.Draw(thumbnailRect);
newImage = UIGraphics.GetImageFromCurrentImageContext();
if (newImage == null)
{
Console.WriteLine("could not scale image");
}
//pop the context to get back to the default
UIGraphics.EndImageContext();
return newImage;
}
Ich habe festgestellt, dass die von Evgenii Kanvets gepostete Swift 3 das Bild nicht einheitlich skaliert.
Hier ist meine Swift 4 Version der Funktion, die das Bild nicht zerquetscht:
static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {
// This function returns a newImage, based on image
// - image is scaled uniformaly to fit into a rect of size newSize
// - if the newSize rect is of a different aspect ratio from the source image
// the new image is cropped to be in the center of the source image
// (the excess source image is removed)
var ratio: CGFloat = 0
var delta: CGFloat = 0
var drawRect = CGRect()
if newSize.width > newSize.height {
ratio = newSize.width / image.size.width
delta = (ratio * image.size.height) - newSize.height
drawRect = CGRect(x: 0, y: -delta / 2, width: newSize.width, height: newSize.height + delta)
} else {
ratio = newSize.height / image.size.height
delta = (ratio * image.size.width) - newSize.width
drawRect = CGRect(x: -delta / 2, y: 0, width: newSize.width + delta, height: newSize.height)
}
UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
image.draw(in: drawRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Der folgende einfache Code hat für mich funktioniert.
[imageView setContentMode:UIViewContentModeScaleAspectFill];
[imageView setClipsToBounds:YES];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,ScreenWidth,ScreenHeigth)];
[scrollView setBackgroundColor:[UIColor blackColor]];
[scrollView setDelegate:self];
[scrollView setShowsHorizontalScrollIndicator:NO];
[scrollView setShowsVerticalScrollIndicator:NO];
[scrollView setMaximumZoomScale:2.0];
image=[image scaleToSize:CGSizeMake(ScreenWidth, ScreenHeigth)];
imageView = [[UIImageView alloc] initWithImage:image];
UIImageView* imageViewBk = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"background.png"]];
[self.view addSubview:imageViewBk];
CGRect rect;
rect.Origin.x=0;
rect.Origin.y=0;
rect.size.width = image.size.width;
rect.size.height = image.size.height;
[imageView setFrame:rect];
[scrollView setContentSize:[imageView frame].size];
[scrollView setMinimumZoomScale:[scrollView frame].size.width / [imageView frame].size.width];
[scrollView setZoomScale:[scrollView minimumZoomScale]];
[scrollView addSubview:imageView];
[[self view] addSubview:scrollView];
dann können Sie auf diese Weise Screenshots zu Ihrem Bild machen
float zoomScale = 1.0 / [scrollView zoomScale];
CGRect rect;
rect.Origin.x = [scrollView contentOffset].x * zoomScale;
rect.Origin.y = [scrollView contentOffset].y * zoomScale;
rect.size.width = [scrollView bounds].size.width * zoomScale;
rect.size.height = [scrollView bounds].size.height * zoomScale;
CGImageRef cr = CGImageCreateWithImageInRect([[imageView image] CGImage], rect);
UIImage *cropped = [UIImage imageWithCGImage:cr];
CGImageRelease(cr);
Hier ist eine Swift 3 Version von Sam Wirchs Leitfaden für Swift gepostet von William T.
extension UIImage {
static func resizedCroppedImage(image: UIImage, newSize:CGSize) -> UIImage? {
var ratio: CGFloat = 0
var delta: CGFloat = 0
var offset = CGPoint.zero
if image.size.width > image.size.height {
ratio = newSize.width / image.size.width
delta = (ratio * image.size.width) - (ratio * image.size.height)
offset = CGPoint(x: delta / 2, y: 0)
} else {
ratio = newSize.width / image.size.height
delta = (ratio * image.size.height) - (ratio * image.size.width)
offset = CGPoint(x: 0, y: delta / 2)
}
let clipRect = CGRect(x: -offset.x, y: -offset.y, width: (ratio * image.size.width) + delta, height: (ratio * image.size.height) + delta)
UIGraphicsBeginImageContextWithOptions(newSize, true, 0.0)
UIRectClip(clipRect)
image.draw(in: clipRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
Diese Frage scheint zur Ruhe gekommen zu sein, aber auf meiner Suche nach einer Lösung, die ich besser verstehen konnte (und die in Swift geschrieben wurde), bin ich zu folgendem Ergebnis gelangt (siehe auch: Wie beschneide ich das UIImage? )
Ich wollte in der Lage sein, aus einer Region basierend auf einem Seitenverhältnis zu beschneiden und auf eine Größe basierend auf einem äußeren Begrenzungsumfang zu skalieren. Hier ist meine Variante:
import AVFoundation
import ImageIO
class Image {
class func crop(image:UIImage, crop source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage {
let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source)
let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(Origin: CGPointZero, size: outputExtent))
let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen
UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale)
let scale = max(
targetRect.size.width / sourceRect.size.width,
targetRect.size.height / sourceRect.size.height)
let drawRect = CGRect(Origin: -sourceRect.Origin * scale, size: image.size * scale)
image.drawInRect(drawRect)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
}
}
Es gibt ein paar Dinge, die ich verwirrend fand, die getrennten Belange des Zuschneidens und der Größenänderung. Das Zuschneiden wird mit dem Ursprung des Rect behandelt, den Sie an drawInRect übergeben, und das Skalieren wird durch den Größenanteil behandelt. In meinem Fall musste ich die Größe des Cropping Rect auf der Quelle mit meinem Output Rect mit dem gleichen Seitenverhältnis in Beziehung setzen. Der Skalierungsfaktor wird dann ausgegeben/eingegeben und muss auf drawRect angewendet (an drawInRect übergeben) werden.
Eine Einschränkung ist, dass bei diesem Ansatz davon ausgegangen wird, dass das von Ihnen gezeichnete Bild größer als der Bildkontext ist. Ich habe dies nicht getestet, aber ich denke, Sie können diesen Code zum Beschneiden/Zoomen verwenden, aber den Skalierungsparameter explizit als den oben genannten Skalierungsparameter definieren. Standardmäßig wendet UIKit einen Multiplikator an, der auf der Bildschirmauflösung basiert.
Abschließend sollte angemerkt werden, dass dieser UIKit-Ansatz höher ist als der CoreGraphics/Quartz- und Core Image-Ansatz und Probleme mit der Bildorientierung zu behandeln scheint. Es ist auch erwähnenswert, dass es, nach ImageIO, ziemlich schnell ist: http://nshipster.com/image-resizing/
- (UIImage*)imageScale:(CGFloat)scaleFactor cropForSize:(CGSize)targetSize
{
targetSize = !targetSize.width?self.size:targetSize;
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.size.width = targetSize.width*scaleFactor;
thumbnailRect.size.height = targetSize.height*scaleFactor;
CGFloat xOffset = (targetSize.width- thumbnailRect.size.width)/2;
CGFloat yOffset = (targetSize.height- thumbnailRect.size.height)/2;
thumbnailRect.Origin = CGPointMake(xOffset,yOffset);
[self drawInRect:thumbnailRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
if(newImage == nil)
{
NSLog(@"could not scale image");
}
UIGraphicsEndImageContext();
return newImage;
}
Unterhalb des Arbeitsbeispiels: Linkes Bild - (Ursprungsbild); Rechtes Bild mit Skala x2
Wenn Sie das Bild skalieren möchten, aber den Rahmen (Proportionen) beibehalten möchten, rufen Sie die Methode folgendermaßen auf:
[yourImage imageScale:2.0f cropForSize:CGSizeZero];
Schnelle Version:
static func imageWithImage(image:UIImage, newSize:CGSize) ->UIImage {
UIGraphicsBeginImageContextWithOptions(newSize, true, UIScreen.mainScreen().scale);
image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage
}