Что действительно происходит, так это то, что фактическая (не нулевая) структура Nullable назначается переменной, поэтому переменная не равна null, но имеет значение.
Самый простой способ - установить рамку вашего UIImageView
и установить contentMode
в один из вариантов изменения размера.
Или вы можете использовать этот служебный метод, если вам действительно нужно изменить размер изображения:
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Пример использования:
#import "MYUtil.h"
…
UIImage *myIcon = [MYUtil imageWithImage:myUIImageInstance scaledToSize:CGSizeMake(20, 20)];
Я обнаружил, что трудно найти ответ, который вы можете использовать «из коробки» в своем проекте Swift 3 . Основная проблема других ответов в том, что они не соблюдают альфа-канал изображения. Вот техника, которую я использую в своих проектах.
extension UIImage {
func scaledToFit(toSize newSize: CGSize) -> UIImage {
if (size.width < newSize.width && size.height < newSize.height) {
return copy() as! UIImage
}
let widthScale = newSize.width / size.width
let heightScale = newSize.height / size.height
let scaleFactor = widthScale < heightScale ? widthScale : heightScale
let scaledSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)
return self.scaled(toSize: scaledSize, in: CGRect(x: 0.0, y: 0.0, width: scaledSize.width, height: scaledSize.height))
}
func scaled(toSize newSize: CGSize, in rect: CGRect) -> UIImage {
if UIScreen.main.scale == 2.0 {
UIGraphicsBeginImageContextWithOptions(newSize, !hasAlphaChannel, 2.0)
}
else {
UIGraphicsBeginImageContext(newSize)
}
draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage ?? UIImage()
}
var hasAlphaChannel: Bool {
guard let alpha = cgImage?.alphaInfo else {
return false
}
return alpha == CGImageAlphaInfo.first ||
alpha == CGImageAlphaInfo.last ||
alpha == CGImageAlphaInfo.premultipliedFirst ||
alpha == CGImageAlphaInfo.premultipliedLast
}
}
Пример использования:
override func viewDidLoad() {
super.viewDidLoad()
let size = CGSize(width: 14.0, height: 14.0)
if let image = UIImage(named: "barbell")?.scaledToFit(toSize: size) {
let imageView = UIImageView(image: image)
imageView.center = CGPoint(x: 100, y: 100)
view.addSubview(imageView)
}
}
Этот код является переписанным расширения Apple с добавленной поддержкой изображений с альфа-каналом и без него.
В качестве дальнейшего чтения я рекомендую проверить эту статью для различных методов изменения размера изображения. Текущий подход предлагает достойную производительность, он работает с высокоуровневыми API и легок для понимания. Я рекомендую придерживаться этого, если только вы не обнаружите, что изменение размера изображения является узким местом в вашей работе.
Swift 4 ответ:
func scaleDown(image: UIImage, withSize: CGSize) -> UIImage {
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(withSize, false, scale)
image.draw(in: CGRect(x: 0, y: 0, width: withSize.width, height: withSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
Здесь мой несколько многословный код Swift
func scaleImage(image:UIImage, toSize:CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(toSize, false, 0.0);
let aspectRatioAwareSize = self.aspectRatioAwareSize(image.size, boxSize: toSize, useLetterBox: false)
let leftMargin = (toSize.width - aspectRatioAwareSize.width) * 0.5
let topMargin = (toSize.height - aspectRatioAwareSize.height) * 0.5
image.drawInRect(CGRectMake(leftMargin, topMargin, aspectRatioAwareSize.width , aspectRatioAwareSize.height))
let retVal = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return retVal
}
func aspectRatioAwareSize(imageSize: CGSize, boxSize: CGSize, useLetterBox: Bool) -> CGSize {
// aspect ratio aware size
// http://stackoverflow.com/a/6565988/8047
let imageWidth = imageSize.width
let imageHeight = imageSize.height
let containerWidth = boxSize.width
let containerHeight = boxSize.height
let imageAspectRatio = imageWidth/imageHeight
let containerAspectRatio = containerWidth/containerHeight
let retVal : CGSize
// use the else at your own risk: it seems to work, but I don't know
// the math
if (useLetterBox) {
retVal = containerAspectRatio > imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
} else {
retVal = containerAspectRatio < imageAspectRatio ? CGSizeMake(imageWidth * containerHeight / imageHeight, containerHeight) : CGSizeMake(containerWidth, imageHeight * containerWidth / imageWidth)
}
return retVal
}
[cf Chris] Чтобы изменить размер до до желаемого размера:
UIImage *after = [UIImage imageWithCGImage:before.CGImage
scale:CGImageGetHeight(before.CGImage)/DESIREDHEIGHT
orientation:UIImageOrientationUp];
или, что эквивалентно, заменить CGImageGetWidth(...)/DESIREDWIDTH
Для моих собратьев-ксамарианцев, вот Xamarin.iOS C # версия ответа @Paul Lynch.
private UIImage ResizeImage(UIImage image, CGSize newSize)
{
UIGraphics.BeginImageContextWithOptions(newSize, false, 0.0f);
image.Draw(new CGRect(0, 0, newSize.Width, newSize.Height));
UIImage newImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return newImage;
}
Swift 2.0:
let image = UIImage(named: "imageName")
let newSize = CGSize(width: 10, height: 10)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image?.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let imageResized = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
использовать это расширение
extension UIImage {
public func resize(size:CGSize, completionHandler:(resizedImage:UIImage, data:NSData?)->()) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
let newSize:CGSize = size
let rect = CGRectMake(0, 0, newSize.width, newSize.height)
UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
self.drawInRect(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let imageData = UIImageJPEGRepresentation(newImage, 0.5)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(resizedImage: newImage, data:imageData)
})
})
}
}
@Paul Lynch - отличный ответ, но он изменит соотношение изображения. если вы не хотите менять соотношение изображения и хотите, чтобы новое изображение соответствовало новому размеру, попробуйте это.
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
// calculate a new size which ratio is same to original image
CGFloat ratioW = image.size.width / newSize.width;
CGFloat ratioH = image.size.height / newSize.height;
CGFloat ratio = image.size.width / image.size.height;
CGSize showSize = CGSizeZero;
if (ratioW > 1 && ratioH > 1) {
if (ratioW > ratioH) {
showSize.width = newSize.width;
showSize.height = showSize.width / ratio;
} else {
showSize.height = newSize.height;
showSize.width = showSize.height * ratio;
}
} else if (ratioW > 1) {
showSize.width = showSize.width;
showSize.height = showSize.width / ratio;
} else if (ratioH > 1) {
showSize.height = showSize.height;
showSize.width = showSize.height * ratio;
}
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(showSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, showSize.width, showSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;}
Эффективный подход без растяжения изображения Swift 4
// Method to resize image
func resize(image: UIImage, toScaleSize:CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(toScaleSize, true, image.scale)
image.draw(in: CGRect(x: 0, y: 0, width: toScaleSize.width, height: toScaleSize.height))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage!
}
// Вызов метода
let resizedImage = self.resize(image: UIImage(named: "YourImageName")!, toScaleSize: CGSize(width: 290, height: 390))
Swift 3.0 с отказоустойчивой опцией (возвращает исходное изображение в случае ошибки):
func resize(image: UIImage, toSize size: CGSize) -> UIImage{
UIGraphicsBeginImageContextWithOptions(size,false,1.0)
image.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
if let resizedImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
return resizedImage
}
UIGraphicsEndImageContext()
return image
}
Rogerio Chaves отвечают как быстрое расширение
func scaledTo(size: CGSize) -> UIImage{
UIGraphicsBeginImageContextWithOptions(size, false, 0.0);
self.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
А также бонус
func scaledTo(height: CGFloat) -> UIImage{
let width = height*self.size.width/self.size.height
return scaledTo(size: CGSize(width: width, height: height))
}
(совместимо с Swift 4) iOS 10+ и iOS < 10 решение (используя UIGraphicsImageRenderer
, если возможно, UIGraphicsGetImageFromCurrentImageContext
в противном случае)
/// Resizes an image
///
/// - Parameter newSize: New size
/// - Returns: Resized image
func scaled(to newSize: CGSize) -> UIImage {
let rect = CGRect(origin: .zero, size: newSize)
if #available(iOS 10, *) {
let renderer = UIGraphicsImageRenderer(size: newSize)
return renderer.image { _ in
self.draw(in: rect)
}
} else {
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage
{
let scale = newWidth / image.size.width
let newHeight = image.size.height * scale
UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
image.drawInRect(CGRectMake(0, 0, newWidth, newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Если вы хотите сделать миниатюру UIImage (с пропорциональным изменением размера или, возможно, с некоторым обрезанием), проверьте категорию UIImage + Resize , которая позволяет вам использовать краткий, подобный ImageMagick-подобный синтаксис:
UIImage* squareImage = [image resizedImageByMagick: @"320x320#"];
Это расширение UIImage, совместимое с Swift 3 и Swift 4 , которое масштабирует изображение до заданного размера с соотношением сторон
extension UIImage {
func scaledImage(withSize size: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
return UIGraphicsGetImageFromCurrentImageContext()!
}
func scaleImageToFitSize(size: CGSize) -> UIImage {
let aspect = self.size.width / self.size.height
if size.width / aspect <= size.height {
return scaledImage(withSize: CGSize(width: size.width, height: size.width / aspect))
} else {
return scaledImage(withSize: CGSize(width: size.height * aspect, height: size.height))
}
}
}
Пример использования
let image = UIImage(named: "apple")
let scaledImage = image.scaleImageToFitSize(size: CGSize(width: 45.0, height: 45.0))
Более компактная версия для Swift 4 и iOS 10 +:
extension UIImage {
func resized(to size: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { _ in
draw(in: CGRect(origin: .zero, size: size))
}
}
}
Использование:
let resizedImage = image.resized(to: CGSize(width: 50, height: 50))
Я нашел категорию для UIImage в собственных примерах Apple, которая делает то же самое. Вот ссылка: https://developer.apple.com/library/ios/samplecode/sc2273/Listings/AirDropSample_UIImage_Resize_m.html .
Вам просто нужно изменить вызов:
UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);
в imageWithImage:scaledToSize:inRect:
с помощью:
UIGraphicsBeginImageContextWithOptions(newSize, NO, 2.0);
, чтобы рассмотреть альфа-канал на изображении.
Если вы просто хотите, чтобы изображение было меньше и не заботились о точном размере:
+ (UIImage *)imageWithImage:(UIImage *)image scaledToScale:(CGFloat)scale
{
UIGraphicsBeginImageContextWithOptions(self.size, YES, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Установка масштаба на 0.25f
даст вам изображение 816 на 612 с 8-мегапиксельной камеры.
Вот категория UIImage + Scale для тех, кто в ней нуждается.
Почему так сложно? Я думаю, что использование системного API может привести к тому же результату:
UIImage *largeImage;
CGFloat ratio = 0.4; // you want to get a new image that is 40% the size of large image.
UIImage *newImage = [UIImage imageWithCGImage:largeImage.CGImage
scale:1/ratio
orientation:largeImage.imageOrientation];
// notice the second argument, it is 1/ratio, not ratio.
Единственное, что нужно, это то, что вы должны передать обратное отношение цели в качестве второго аргумента, так как согласно документу второй параметр определяет отношение исходного изображения по сравнению с новым масштабированным.
Вот модификация категории, написанной iWasRobbed выше. Он сохраняет пропорции исходного изображения, а не искажает его.
- (UIImage*)scaleToSizeKeepAspect:(CGSize)size {
UIGraphicsBeginImageContext(size);
CGFloat ws = size.width/self.size.width;
CGFloat hs = size.height/self.size.height;
if (ws > hs) {
ws = hs/ws;
hs = 1.0;
} else {
hs = ws/hs;
ws = 1.0;
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(size.width/2-(size.width*ws)/2,
size.height/2-(size.height*hs)/2, size.width*ws,
size.height*hs), self.CGImage);
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
Вот простой способ:
UIImage * image = [UIImage imageNamed:@"image"];
CGSize sacleSize = CGSizeMake(10, 10);
UIGraphicsBeginImageContextWithOptions(sacleSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, sacleSize.width, sacleSize.height)];
UIImage * resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
resizedImage - это новое изображение.
Я также видел, что это также сделано (который я использую в UIButtons
для нормального и выделенного состояния, поскольку кнопки не подходят resize
). Благодарность идет тому, кем был первоначальный автор.
Сначала создайте пустой файл .h и .m с именами UIImageResizing.h
и UIImageResizing.m
// Put this in UIImageResizing.h
@interface UIImage (Resize)
- (UIImage*)scaleToSize:(CGSize)size;
@end
// Put this in UIImageResizing.m
@implementation UIImage (Resize)
- (UIImage*)scaleToSize:(CGSize)size {
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, size.width, size.height), self.CGImage);
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
@end
Включите этот файл .h в любой файл .m, в котором вы собираетесь использовать функцию и затем назовите это так:
UIImage* image = [UIImage imageNamed:@"largeImage.png"];
UIImage* smallImage = [image scaleToSize:CGSizeMake(100.0f,100.0f)];
Это улучшение кода Пола даст вам четкое изображение с высоким разрешением на iPhone с дисплеем сетчатки. В противном случае при уменьшении это размыто.
+ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
UIGraphicsBeginImageContextWithOptions(newSize, YES, 2.0);
} else {
UIGraphicsBeginImageContext(newSize);
}
} else {
UIGraphicsBeginImageContext(newSize);
}
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Тревор Ховард имеет несколько категорий UIImage , которые прекрасно справляются с изменением размера. Если ничего другого, вы можете использовать код в качестве примера.
Примечание. Начиная с iOS 5.1, этот ответ может быть недействительным. См. Комментарий ниже.
Правильное Swift 3.0 для iOS 10+ решение: Использование ImageRenderer
и синтаксис закрытия:
func imageWith(newSize: CGSize) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: newSize)
let image = renderer.image { _ in
self.draw(in: CGRect.init(origin: CGPoint.zero, size: newSize))
}
return image
}
А вот версия Objective C:
@implementation UIImage (ResizeCategory)
- (UIImage *)imageWithSize:(CGSize)newSize
{
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:newSize];
UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext*_Nonnull myContext) {
[self drawInRect:(CGRect) {.origin = CGPointZero, .size = newSize}];
}];
return image;
}
@end
Вот версия Swift Ответ Пола Линча
func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
image.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
Версия Swift 3.0:
func imageWithImage(image:UIImage, scaledToSize newSize:CGSize) -> UIImage{
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
image.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: newSize.width, height: newSize.height)))
let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
extension UIImage {
enum ContentMode {
case contentFill
case contentAspectFill
case contentAspectFit
}
func resize(withSize size: CGSize, contentMode: ContentMode = .contentAspectFill) -> UIImage? {
let aspectWidth = size.width / self.size.width;
let aspectHeight = size.height / self.size.height;
switch contentMode {
case .contentFill:
return resize(withSize: size)
case .contentAspectFit:
let aspectRatio = min(aspectWidth, aspectHeight)
return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
case .contentAspectFill:
let aspectRatio = max(aspectWidth, aspectHeight)
return resize(withSize: CGSize(width: self.size.width * aspectRatio, height: self.size.height * aspectRatio))
}
}
private func resize(withSize size: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 1)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
и для использования вы можете сделать следующее:
let image = UIImage(named: "image.png")!
let newImage = image.resize(withSize: CGSize(width: 200, height: 150), contentMode: .contentAspectFill)
Благодаря abdullahselek за его оригинальное решение.