Add chess board to transparent parts of an UIImageView - swift

I have an UIImageView, which shows an UIImage, having content mode as Aspect Fit.
I need to indicate transparent areas of the images with a chessboard.
How to obtain squares of the same size, no matter the size of the image?
extension UIImage {
func withChessboard() -> UIImage? {
var image: UIImage?
let l = 4
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let imageRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
if let context = UIGraphicsGetCurrentContext() {
context.setFillColor(UIColor.lightGray.cgColor)
for row in 0 ..< Int(size.width) {
for col in 0 ..< Int(size.height) {
if (row + col) % 2 == 0 {
context.fill(CGRect(x: col * l, y: row * l, width: l, height: l))
}
}
}
draw(in: imageRect, blendMode: .normal, alpha: 1.0)
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
return nil
}
}
Now I have this ( first image being smaller )
and this:
EDIT, using the solution suggested by Trent, I have obtained this. I need the tiles to cover just the size of the image, not the whole imageview.

Looks like this existing post has a clean answer to your solution.
Basically show you how to create a pattern with UIColor and set that as the backgroundColor of the UIImageView.
Let me know if that helps! If not, I'd like to hear why.

Related

How can I use CGRect() dynamically?

I have the following scene example where I can crop an image based on the selection (red square).
That square has dynamic Height and Width - base on this fact I want to use the selected Height and Width to crop what is inside of the Red square.
The function that I am using for cropping is from Apple developer and looks like this:
func cropImage(_ inputImage: UIImage, toRect cropRect: CGRect, viewWidth: CGFloat, viewHeight: CGFloat) -> UIImage?
{
let imageViewScale = max(inputImage.size.width / viewWidth,
inputImage.size.height / viewHeight)
// Scale cropRect to handle images larger than shown-on-screen size
let cropZone = CGRect(x:cropRect.origin.x * imageViewScale,
y:cropRect.origin.y * imageViewScale,
width:cropRect.size.width * imageViewScale,
height:cropRect.size.height * imageViewScale)
// Perform cropping in Core Graphics
guard let cutImageRef: CGImage = inputImage.cgImage?.cropping(to:cropZone)
else {
return nil
}
// Return image to UIImage
let croppedImage: UIImage = UIImage(cgImage: cutImageRef)
return croppedImage
}
Now. I want to use the given Height and Width to crop that selection.
let croppedImage = cropImage(image!, toRect: CGRect(x:?? , y:?? , width: ??, height: ??), viewWidth: ??, viewHeight: ??)
What should I fill in these parameters in order to crop the image based on the above dynamic selection?
Ok, since you just have info of width and height of the cropping shape. You'll need to calculate the x and y by yourself.
First, let's consider these information:
// let's pretend this is a sample of size that your crop tool provides to you
let cropSize = CGSize(width: 120, height: 260)
Next, you'll need to obtain the display size (width and height) of your image. Display size here is the frame's size of your image, not the size of the image itself.
// again, lets pretend it's just a frame size of your image
let imageSize = CGSize(width: 320, height: 480)
With this info, you can obtain the x and y necessary to compose a CGRect and then, provide to a cropping function you desire.
let x = (imageSize.width - cropSize.width) / 2
let y = (imageSize.height - cropSize.height) / 2
So now, you can create a rectangle to crop your image like this:
let cropRect = CGRect(x: x, y: y, width: cropSize.width, height: cropSize.height)
With cropRect you can use on both cropping or cropImage functions mentioned in your question.
Ok, let's assume that your image is in imageView, wich is located somewhere in your screen. The rect is a variable where your selected frame (related to the imageView.frame) is stored. So the result is:
let croppedImage = cropImage(image!, toRect: rect, viewWidth: imageView.width, viewHeight: imageView.height)
I've used the info from all of your answers and especially #matt's comment and this is the final solution.
Using the input values that my red square returned, I've adapted the original Crop function to this one:
func cropImage(_ inputImage: UIImage, width: Double, height: Double) -> UIImage?
{
let imsize = inputImage.size
let ivsize = UIScreen.main.bounds.size
var scale : CGFloat = ivsize.width / imsize.width
if imsize.height * scale < ivsize.height {
scale = ivsize.height / imsize.height
}
let croppedImsize = CGSize(width:height/scale, height:width/scale)
let croppedImrect =
CGRect(origin: CGPoint(x: (imsize.width-croppedImsize.width)/2.0,
y: (imsize.height-croppedImsize.height)/2.4),
size: croppedImsize)
let r = UIGraphicsImageRenderer(size:croppedImsize)
let croppedIm = r.image { _ in
inputImage.draw(at: CGPoint(x:-croppedImrect.origin.x, y:-croppedImrect.origin.y))
}
return croppedIm
}

How to combine two UIImages into a single Image in Swift?

I've been trying to merge two images where one is on top and the other at the bottom. This code below doesn't seem to work. The x coordinator is correct but the y doesn't seem right and it crops the top image when I alter it. What am I doing wrong?
func combine(bottomImage: Data, topImage: Data) -> UIImage {
let bottomImage = UIImage(data: topImage)
let topImage = UIImage(data: bottomImage)
let size = CGSize(width: bottomImage!.size.width, height: bottomImage!.size.height + topImage!.size.height)
UIGraphicsBeginImageContext(size)
let areaSizeb = CGRect(x: 0, y: 0, width: bottomImage!.size.width, height: bottomImage!.size.height)
let areaSize = CGRect(x: 0, y: 0, width: topImage!.size.width, height: topImage!.size.height)
bottomImage!.draw(in: areaSizeb)
topImage!.draw(in: areaSize)
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
You are drawing both images into the same rect. You also should not use force-unwrapping. That causes your app to crash if anything goes wrong.
There are also various other small mistakes.
Change your function like this:
// Return an Optional so we can return nil if something goes wrong
func combine(bottomImage: Data, topImage: Data) -> UIImage? {
// Use a guard statement to make sure
// the data can be converted to images
guard
let bottomImage = UIImage(data: bottomImage),
let topImage = UIImage(data: topImage) else {
return nil
}
// Use a width wide enough for the widest image
let width = max(bottomImage.size.width, topImage.size.width)
// Make the height tall enough to stack the images on top of each other.
let size = CGSize(width: width, height: bottomImage.size.height + topImage.size.height)
UIGraphicsBeginImageContext(size)
let bottomRect = CGRect(
x: 0,
y: 0,
width: bottomImage.size.width,
height: bottomImage.size.height)
// Position the bottom image under the top image.
let topRect = CGRect(
x: 0,
y: bottomImage.size.height,
width: topImage.size.width,
height: topImage.size.height)
bottomImage.draw(in: bottomRect)
topImage!.draw(in: topRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
(And you should really be using a UIGraphicsImageRenderer rather than than calling UIGraphicsBeginImageContext()/ UIGraphicsEndImageContext().)
Edit:
Note that if the 2 images are different widths the above code will leave a "dead space" on the right of the narrower image. You could also make the code center the narrower image, or scale it up to be the same width. (If you do scale it up I suggest scaling it up in both dimensions to preserve the original aspect ratio. Otherwise it will look stretched and unnatural.)

How to take a snapshot from UIImage of a UIImageView that is scaleAspectFit mode?

I have an image in a UIImageView:
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .red
because of .scaleAspectFit the image view has some red borders and thats OK:
User can added some UIView like label or images over the imageView.
In final step I used the following code to save edited image and user can share it or save it to photo library:
private func generateImage() -> UIImage? {
var finalImage: UIImage?
UIGraphicsBeginImageContextWithOptions(CGSize(width: imageView.frame.size.width, height: imageView.frame.size.height), true, 0)
imageView.drawHierarchy(in: CGRect(x: 0, y: 0, width: imageView.frame.size.width, height: imageView.frame.size.height), afterScreenUpdates: true)
finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
The problem is that the finalImage still has the red borders from imageView.
You can get CGRect of the UIImage displayed in the UIImageView in AspectFit content mode. Please create extension of UIImageView like this,
extension UIImageView {
var contentClippingRect: CGRect {
guard let image = image else { return bounds }
guard contentMode == .scaleAspectFit else { return bounds }
guard image.size.width > 0 && image.size.height > 0 else { return bounds }
let scale: CGFloat
if image.size.width > image.size.height {
scale = bounds.width / image.size.width
} else {
scale = bounds.height / image.size.height
}
let size = CGSize(width: image.size.width * scale, height: image.size.height * scale)
let x = (bounds.width - size.width) / 2.0
let y = (bounds.height - size.height) / 2.0
return CGRect(x: x, y: y, width: size.width, height: size.height)
}
}
You can now use imageView.contentClippingRect to read how read the position and size of the image inside.
You have to do minor changes in your method, call your function with appropriate bounds as contentClippingRect.
Let me know in case of any queries.
UPDATE
Please try this UIImageView+Extension, this might help you. It is in Objective-C code, convert it in Swift.
You can try this as well,
let image = #imageLiteral(resourceName: "Cat03")
let x: CGRect = AVMakeRect(aspectRatio: image.size, insideRect: imageView1.frame)
print(x)
Above code gives you size perfectly.

Cropping an image from the top in Swift

I am trying to crop this image, which is a SKSpriteNode:
I am trying to crop this image from the top, so that I maintain the bottom semi circle of this shape. For instance, it'd be cropped to this:
So I use these two methods to accomplish this task:
func recalculateScore() {
currentScore -= decreaseRate
let image = UIImage(cgImage: (vial.texture?.cgImage())!)
vial.texture = SKTexture(image: cropBottomImage(image: image))
}
func cropBottomImage(image: UIImage) -> UIImage {
let height = CGFloat(image.size.height / 3)
let rect = CGRect(x: 0, y: image.size.height - height, width: image.size.width, height: height)
return cropImage(image: image, toRect: rect)
}
func cropImage(image:UIImage, toRect rect:CGRect) -> UIImage {
let imageRef:CGImage = image.cgImage!.cropping(to: rect)!
let croppedImage:UIImage = UIImage(cgImage:imageRef)
return croppedImage
}
However, this leads to this result:
It is as if it was being compressed. I think my issue might be in this line:
let rect = CGRect(x: 0, y: image.size.height - height, width: image.size.width, height: height)
Does the CGRect coordinate of (0,0) lie within the top most left corner? I am a bit confused on what the x and y parameters for the CGRect mean?
Resize your sprite, what is happening is the cropped texture is stretching to fill the sprite, and since you only crop vertically, it will only stretch vertically
func recalculateScore() {
currentScore -= decreaseRate
let image = UIImage(cgImage: (vial.texture?.cgImage())!)
vial.texture = SKTexture(image: cropBottomImage(image: image))
vial.size = vial.texture.size
}

how to change specific color in image to a different color

I have an UIView that its layer contents is an image.
let image = UIImage(names: "myImage")
layer.contents = image.CGImage
This image has a few colors.
Is there a way to change a specific color to any other color of my choice?
I found answers for changing the all of the colors in the image but not a specific one.
answer
You can't change specific color in PNG with transparent background, but.. i found the solution.
extension UIImage {
func maskWithColors(color: UIColor) -> UIImage? {
let maskingColors: [CGFloat] = [100, 255, 100, 255, 100, 255] // We should replace white color.
let maskImage = cgImage! //
let bounds = CGRect(x: 0, y: 0, width: size.width * 3, height: size.height * 3) // * 3, for best resolution.
let sz = CGSize(width: size.width * 3, height: size.height * 3) // Size.
var returnImage: UIImage? // Image, to return
/* Firstly we will remove transparent background, because
maskingColorComponents don't work with transparent images. */
UIGraphicsBeginImageContextWithOptions(sz, true, 0.0)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
context.scaleBy(x: 1.0, y: -1.0) // iOS flips images upside down, this fix it.
context.translateBy(x: 0, y: -sz.height) // and this :)
context.draw(maskImage, in: bounds)
context.restoreGState()
let noAlphaImage = UIGraphicsGetImageFromCurrentImageContext() // new image, without transparent elements.
UIGraphicsEndImageContext()
let noAlphaCGRef = noAlphaImage?.cgImage // get CGImage.
if let imgRefCopy = noAlphaCGRef?.copy(maskingColorComponents: maskingColors) { // Magic.
UIGraphicsBeginImageContextWithOptions(sz, false, 0.0)
let context = UIGraphicsGetCurrentContext()!
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: 0, y: -sz.height)
context.clip(to: bounds, mask: maskImage) // Remove background from image with mask.
context.setFillColor(color.cgColor) // set new color. We remove white color, and set red.
context.fill(bounds)
context.draw(imgRefCopy, in: bounds) // draw new image
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
returnImage = finalImage! // YEAH!
UIGraphicsEndImageContext()
}
return returnImage
}
}
For call this function use code like this...
let image = UIImage(named: "Brush").maskWithColor(color: UIColor.red)
Result:
You can not change the image color... The only way is to change the image on any event or something...
Another variant is to create one image with transparent color, and set the background color of the view or something where you put the image...