I am learning Swift and I am creating an app that uses a personal photo and puts another on top of it. I now have a hacky solution, to create a screenshot of the area and save it. I need to do this in Swift
#IBAction func saveImage(sender: AnyObject) {
//Create the UIImage
UIGraphicsBeginImageContext(imageView.frame.size)
view.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//Save it to the camera roll
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
But, this was working and isn't anymore. But, this is also not the best solution.
So guys, how can I save an image to the camera roll from a personal image, with an image as overlay?
Help would be greatly appreciated!! Thanks!
I would recommend reading through this thread. All your answers are there. Once you read through that article, the following code sample should help you composite the two images together properly.
func saveImage() {
let bottomImage = UIImage(named: "bottom")!
let topImage = UIImage(named: "top")!
let newSize = CGSizeMake(100, 100) // set this to what you need
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
bottomImage.drawInRect(CGRect(origin: CGPointZero, size: newSize))
topImage.drawInRect(CGRect(origin: CGPointZero, size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
Hopefully this gets you going in the right direction.
Apple advises against UIGraphicsBeginImageContext, so as long as your app does not support devices older than iOS 10, then use something like this:
private func drawLogoIn(_ image: UIImage, _ logo: UIImage, position: CGPoint) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: image.size)
return renderer.image { context in
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
logo.draw(in: CGRect(origin: position, size: logo.size))
}
}
Besides performance gains, you get full P3 range.
UPDATE FOR SWIFT 4
func saveImage() {
let bottomImage = UIImage(named: "your bottom image name")!
let topImage = UIImage(named: "your top image name")!
let newSize = CGSize(width: 100, height: 100) // set this to what you need
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
bottomImage.draw(in: CGRect(origin: CGPoint.zero, size: newSize))
topImage.draw(in: CGRect(origin: CGPoint.zero, size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
To use the image just refer to newImage
EXAMPLE HOW TO USE THE IMAGE:
#IBOutlet weak var imageButton: UIButton!
imageButton.setBackgroundImage(newImage), for: .normal)
This is an edit of cnoon's answer but optimized for Swift 4.
Updated to Swift 3.0:
func saveImage() {
let bottomImage = UIImage(named: "bottom")!
let topImage = UIImage(named: "top")!
let newSize = CGSizeMake(100, 100) // set this to what you need
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
bottomImage.draw(in: CGRect(origin: CGPointZero, size: newSize))//As drawInRect is deprecated
topImage.draw(at: CGRect(origin: CGPointZero, size: newSize))//As drawInRect is deprecated
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
Related
This is the code I am using
extension UIImage {
var ellipseMasked: UIImage? {
guard let cgImage = cgImage else { return nil }
let rect = CGRect(origin: .zero, size: size)
return UIGraphicsImageRenderer(size: size, format: imageRendererFormat)
.image{ _ in
UIBezierPath(ovalIn: rect).addClip()
UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
.draw(in: rect)
}
}
}
This is the image I got
The background color is black.
How can I make the background transparent?
I tried different ways but haven't made it work yet.
You can subclass UIImageView and mask its CALayer instead of clipping the image itself:
extension CAShapeLayer {
convenience init(path: UIBezierPath) {
self.init()
self.path = path.cgPath
}
}
class EllipsedView: UIImageView {
override func layoutSubviews() {
super.layoutSubviews()
layer.mask = CAShapeLayer(path: .init(ovalIn: bounds))
}
}
let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!
let iv = EllipsedView(image: profilePicture)
edit/update
If you need to clip the UIImage itself you can do it as follow:
extension UIImage {
var ellipseMasked: UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
defer { UIGraphicsEndImageContext() }
UIBezierPath(ovalIn: .init(origin: .zero, size: size)).addClip()
draw(in: .init(origin: .zero, size: size))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
For iOS10+ you can use UIGraphicsImageRenderer.
extension UIImage {
var ellipseMasked: UIImage {
let rect = CGRect(origin: .zero, size: size)
let format = imageRendererFormat
format.opaque = false
return UIGraphicsImageRenderer(size: size, format: format).image{ _ in
UIBezierPath(ovalIn: rect).addClip()
draw(in: rect)
}
}
}
let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!
profilePicture.ellipseMasked
Here are two solutions using SwiftUI.
This solution can be used to clip the image shape to a circle.
Image("imagename").resizable()
.clipShape(Circle())
.scaledToFit()
This solution can be used to get more of an eclipse or oval shape from the image.
Image("imagename").resizable()
.cornerRadius(100)
.scaledToFit()
.padding()
I have a UIScrollView as subview of a view controller main view. I'm trying to save a screenshot of whatever is visible in the frame of the UIScrollView only.
UIGraphicsBeginImageContextWithOptions(imageScrollView.bounds.size, false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
context?.translateBy(x: 0, y: 0)
view.layer.render(in: context!)
let visibleScrollViewImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let popupImageView = UIImageView(image: visibleScrollViewImage)
popupImageView.layer.borderColor = UIColor.white.cgColor
popupImageView.layer.borderWidth = 4
popupImageView.frame = CGRect(origin: CGPoint(x: 0, y: 400), size: CGSize(width: 400, height: 400))
imageScrollView.removeFromSuperview()
view.addSubview(popupImageView)
The part with the popupImageView is just to test out and see what is actually saving, it seems there is some offset problem, the horizontal axis is fine but i seem to be getting just the top third of the image I want, and above that is just dark space.
Seems like it must be a pretty easy solution but I've searched through all similar questions and can't find an answers.
Thanks heaps!
Try the following. If I do it with a UITableView control, it works.
import UIKit
class ViewController: UIViewController {
#IBAction func snapTapped(_ sender: UIButton) {
UIGraphicsBeginImageContextWithOptions(imageScrollView.bounds.size, true, 1.0)
imageScrollView.drawHierarchy(in: CGRect(origin: CGPoint.zero, size: imageScrollView.bounds.size), afterScreenUpdates: true)
if let image = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
myImageView.image = image
}
}
}
func screenShotMethod()->UIImage
{
let layer = self.imageScrollView.layer
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
layer.render(in: UIGraphicsGetCurrentContext()!)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenshot!
}
I'm trying to have a transperant hole in a uiimage This is what I found so far:
let hole = CGRect(x: 0, y: 0, width: 50, height: 50)
let context = UIGraphicsGetCurrentContext()!
context.clear(hole)
myImage.image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
but I get nil at : let context = UIGraphicsGetCurrentContext()!
I understand that I need to define current context somewhere but I'm not sure where and how
You need to have a context available to draw with. Create one explicitly with UIGraphicsBeginImageContext, and when you are done drawing, call UIGraphicsEndImageContext to clean it up.
Here your code, with the call to UIGraphicsBeginImageContext, wrapped in an extension method:
extension UIImage {
func imageWithHole(at rect: CGRect) -> UIImage? {
UIGraphicsBeginImageContext(self.size)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
self.draw(at: CGPoint.zero)
context.clear(rect)
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
}
Use it like this:
let sourceImage = UIImage(named: "sample.jpg")
let imageWithHole = sourceImage?.imageWithHole(at: CGRect(x: 50, y: 50, width: 50, height: 50))
I know there are several other ways to do this; I don't want to import anything that I don't need to. If someone can help me with his code, that would be great.
Currently, it is only saving the original image without the watermark image.
extension UIImage {
class func imageWithWatermark(image1: UIImageView, image2: UIImageView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(image1.bounds.size, false, 0.0)
image2.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image1.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
}
func addWatermark() {
let newImage = UIImage.imageWithWatermark(imageView, image2: watermarkImageView)
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil)
}
EDIT: I've got the watermark appearing on the saved images.
I had to switch the order of the layers:
image1.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image2.layer.renderInContext(UIGraphicsGetCurrentContext()!)
HOWEVER, it is not appearing in the correct place.It seems to always appear in the center of the image.
If you grab the UIImageViews' images you could use the following concept:
if let img = UIImage(named: "image.png"), img2 = UIImage(named: "watermark.png") {
let rect = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
UIGraphicsBeginImageContextWithOptions(img.size, true, 0)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextFillRect(context, rect)
img.drawInRect(rect, blendMode: .Normal, alpha: 1)
img2.drawInRect(CGRectMake(x,y,width,height), blendMode: .Normal, alpha: 1)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(result, nil, nil, nil)
}
SWIFT 4
Use this
let backgroundImage = imageData!
let watermarkImage = #imageLiteral(resourceName: "jodi_url_icon")
let size = backgroundImage.size
let scale = backgroundImage.scale
UIGraphicsBeginImageContextWithOptions(size, false, scale)
backgroundImage.draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
watermarkImage.draw(in: CGRect(x: 10, y: 10, width: size.width, height: size.height - 40))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Use result to UIImageView, tested.
I have searched everywhere and found a few examples on how to mask an image, but none of them seem to work for me... I am running on iOS9 and using Swift2.0
Right now this is what I have:
class func maskImage(background: UIImage, withMask mask: UIImage) -> UIImage {
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue).rawValue
let colorSpace = CGColorSpaceCreateDeviceRGB()!
let context = CGBitmapContextCreate(nil, CGImageGetWidth(mask.CGImage), CGImageGetHeight(mask.CGImage), 8, 0, colorSpace, bitmapInfo)
CGContextDrawImage(context, CGRectMake(0, 0, mask.size.width * mask.scale, mask.size.height * mask.scale), mask.CGImage)
let maskRef: CGImageRef = CGBitmapContextCreateImage(context)!
let masked: CGImageRef = CGImageCreateWithMask(background.CGImage, maskRef)!
let icon: UIImage = UIImage(CGImage: masked, scale: mask.scale, orientation: mask.imageOrientation)
return icon
}
I am passing two pngs in and getting the error: EXC_BAD_INSTRUCTION on the line where I initialize masked. I don't have enough rep to post the image.
I am probably missing something super simple, but would love your help. Thank you!
With Swift 3.0 we have had the great name change....
so this appears to function now after some renaming... and cleaning...
based on this example: ( https://www.innofied.com/implementing-image-masking-in-ios/ )
func maskImage(image:UIImage, mask:UIImage )->UIImage{
let imageReference = (image.cgImage)!
let maskReference = (mask.cgImage)!
let imageMask = CGImage.init(
maskWidth: maskReference.width
,height: maskReference.height
,bitsPerComponent: maskReference.bitsPerComponent
,bitsPerPixel: maskReference.bitsPerPixel
,bytesPerRow: maskReference.bytesPerRow
,provider: maskReference.dataProvider!
,decode: nil
,shouldInterpolate: true
)
return (UIImage(cgImage:(imageReference.masking(imageMask!))!))
}
I am also running the latest versions and had a lot of trouble with this. I was finally able to use the below code to crop part of an image and overlay a puzzle piece with an image mask.
import UIKit
import CoreGraphics
class ViewController2: UIViewController {
#IBOutlet var imageView: UIImageView!
var p1PosX: CGFloat = 0.0
var p1PosY: CGFloat = 0.0
var p1Width: CGFloat = 0.0
var p1Height: CGFloat = 0.0
override func viewDidLoad() {
super.viewDidLoad()
let panda = UIImage(named: "panda.jpeg")!
let puzzle1 = UIImage(named: "PP-4-1.gif")!
//Crop
let contextSize: CGSize = panda.size
let rect = CGRect(x: 0, y: 0, width: 200, height: 200)
p1PosX = 0
p1PosY = 0
p1Width = contextSize.width / 2
p1Height = contextSize.height / 2
let rect1: CGRect = CGRectMake(p1PosX, p1PosY, 350, 350)
let imageRef1: CGImageRef = CGImageCreateWithImageInRect(panda.CGImage, rect1)!
let panda1: UIImage = UIImage(CGImage: imageRef1, scale: panda.scale, orientation: panda.imageOrientation)
imageView.image = panda1
//Mask
UIGraphicsBeginImageContext(imageView.frame.size)
panda1.drawInRect(rect)
puzzle1.drawInRect(CGRectMake(0, 0, imageView.frame.size.width , imageView.frame.size.height), blendMode: CGBlendMode.DestinationIn, alpha: 1.0)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
imageView.image = newImage
}
}
In case anybody stumbles across this same situation and is looking for a clean solution, here's a function to mask an image. It works for .jpg, .png and .gif images.
func maskImage(image:UIImage, mask:(UIImage))->UIImage{
let imageReference = image.CGImage
let maskReference = mask.CGImage
let imageMask = CGImageMaskCreate(CGImageGetWidth(maskReference),
CGImageGetHeight(maskReference),
CGImageGetBitsPerComponent(maskReference),
CGImageGetBitsPerPixel(maskReference),
CGImageGetBytesPerRow(maskReference),
CGImageGetDataProvider(maskReference), nil, true)
let maskedReference = CGImageCreateWithMask(imageReference, imageMask)
let maskedImage = UIImage(CGImage:maskedReference!)
return maskedImage
}
Add the following declaration lines in your viewDidLoad() or init() as needed :
let image = UIImage(named: "image.png")
let maskingImage = UIImage(named: "mask.png")
let imageView = UIImageView(image: maskingImage)
imageView.image = maskImage(image!, mask: maskingImage!)
Thanks to Manish Kumar's solution Implementing Image Masking in iOS