¿Cómo crear dinámicamente una imagen UII de color 1x1 en el iPhone?

120

Me gustaría crear un UIImage 1x1 dinámicamente basado en un UIColor.

Sospecho que esto se puede hacer rápidamente con Quartz2d, ​​y estoy estudiando detenidamente la documentación tratando de comprender los fundamentos. Sin embargo, parece que hay muchas trampas potenciales: no identificar correctamente la cantidad de bits y bytes por cosas, no especificar las banderas correctas, no liberar datos no utilizados, etc.

¿Cómo se puede hacer esto de forma segura con Quartz 2d (u otra forma más sencilla)?

Ian Terrell
fuente

Respuestas:

331

Puedes usar CGContextSetFillColorWithColory CGContextFillRectpara esto:

Rápido

extension UIImage {
    class func image(with color: UIColor) -> UIImage {
        let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()

        CGContextSetFillColorWithColor(context, color.CGColor)
        CGContextFillRect(context, rect)

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image
    }
}

Swift3

extension UIImage {
    class func image(with color: UIColor) -> UIImage {
        let rect = CGRect(origin: CGPoint(x: 0, y:0), size: CGSize(width: 1, height: 1))
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()!

        context.setFillColor(color.cgColor)
        context.fill(rect)

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image!
    }
}

C objetivo

+ (UIImage *)imageWithColor:(UIColor *)color {
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}
Matt Stevens
fuente
19
Bonito :-) Recomendaría poner este método en una categoría como método de clase, luego se puede agregar a un proyecto de manera simple e invocar usando una línea como [UIImage imageWithColor: [UIColor redColor]].
7
En caso de que esté creando estas imágenes en un bucle o usando un tamaño mayor, una optimización sería usar un lienzo opaco solo si el color tiene alfa. Así:const CGFloat alpha = CGColorGetAlpha(color.CGColor); const BOOL opaque = alpha == 1; UIGraphicsBeginImageContextWithOptions(size, opaque, 0);
hpique
proporcione la versión rápida
fnc12
¿Hay alguna manera de hacer que el rápido sea un inicializador personalizado?
Antzi
1
¿Por qué está UIGraphicsGetCurrentContext()regresando nil? Edit : lo tengo, estaba pasando 0por el rect's width.
Iulian Onofrei
9

Aquí hay otra opción basada en el código de Matt Stephen. Crea una imagen de color sólido de tamaño variable de modo que pueda reutilizarla o cambiar su tamaño (por ejemplo, usarla como fondo).

+ (UIImage *)prefix_resizeableImageWithColor:(UIColor *)color {
    CGRect rect = CGRectMake(0.0f, 0.0f, 3.0f, 3.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(1, 1, 1, 1)];

    return image;
}

Ponlo en una categoría UIImage y cambia el prefijo.

Ben Lachman
fuente
6

Usé la respuesta de Matt Steven muchas veces, así que hice una categoría para ella:

@interface UIImage (mxcl)
+ (UIImage *)squareImageWithColor:(UIColor *)color dimension:(int)dimension;
@end

@implementation UIImage (mxcl)
+ (UIImage *)squareImageWithColor:(UIColor *)color dimension:(int)dimension {
    CGRect rect = CGRectMake(0, 0, dimension, dimension);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}
@end
mxcl
fuente
5

Usando el último UIGraphicsImageRenderer de Apple, el código es bastante pequeño:

import UIKit

extension UIImage {
    static func from(color: UIColor) -> UIImage {
        let size = CGSize(width: 1, height: 1)
        return UIGraphicsImageRenderer(size: size).image(actions: { (context) in
            context.cgContext.setFillColor(color.cgColor)
            context.fill(.init(origin: .zero, size: size))
        })
    }
}
bitwit
fuente
0

Para mí, un inicio de conveniencia se siente más ordenado en Swift.

extension UIImage {

    convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {

        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        UIGraphicsBeginImageContext(rect.size)
        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }

        context.setFillColor(color.cgColor)
        context.fill(rect)

        guard let image = context.makeImage() else {
            return nil
        }
        UIGraphicsEndImageContext()

        self.init(cgImage: image)
    }

}
Mark Bridges
fuente
-7

Ok, esto no será exactamente lo que quieres, pero este código dibujará una línea. Puedes adaptarlo para hacer un punto. O al menos obtén un poco de información.

Hacer la imagen 1x1 parece un poco extraño. Los trazos recorren la línea, por lo que un trazo de 1.0 a 0.5 debería funcionar. Solo juega.

- (void)drawLine{

UIGraphicsBeginImageContext(CGSizeMake(320,300));

CGContextRef ctx = UIGraphicsGetCurrentContext();

float x = 0;
float xEnd = 320;
float y = 300;

CGContextClearRect(ctx, CGRectMake(5, 45, 320, 300));

CGContextSetGrayStrokeColor(ctx, 1.0, 1.0);

CGContextSetLineWidth(ctx, 1);
CGPoint line[2] = { CGPointMake(x,y), CGPointMake(xEnd, y) };

CGContextStrokeLineSegments(ctx, line, 2);

UIImage *theImage=UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}
Corey Floyd
fuente