Masking an image in Swift using CALayer and UIImage - swift

I'm programming in Swift. I want to mask an image using CALayer and UIImage. I'm creating my mask image programmatically. The created mask image is a UIImage and works fine when I view it on its own. But when I use it as a mask the whole screen becomes white. I suspect that my problem is in configuring the CALayer object. I would appreciate your help. Thanks!
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var maskImageSize = CGSizeMake(self.imageView.frame.width, self.imageView.frame.height)
UIGraphicsBeginImageContextWithOptions(maskImageSize, false, 0.0)
var color = UIColor(white: 1.0, alpha: 1.0)
color.setFill()
var rect = CGRectMake(0, 0, self.imageView.frame.width, self.imageView.frame.height)
UIRectFill(rect)
color = UIColor(white: 0.0, alpha: 1.0)
color.setFill()
rect = CGRectMake((self.imageView.frame.width/2)-100, (self.imageView.frame.height/2)-100, 200, 200)
UIRectFill(rect)
var maskImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
var maskLayer = CALayer()
maskLayer.contents = maskImage
maskLayer.contentsRect = CGRectMake(0, 0, self.imageView.bounds.width, self.imageView.bounds.height)
self.imageView.image = UIImage(named: "pictobemasked.png")
self.imageView.layer.mask = maskLayer;
}
}

Unfortunately you've asked your question rather badly - you have not said what it is that you are actually trying to do! It looks, however, as if you might be trying to punch a rectangular hole in your image view using a mask. If so, your code has at least three huge flaws.
One reason your code is not working is that a mask is based on transparency, not on color. You are using an opaque white and an opaque black, which are both opaque, so there is no difference there. You need your two colors to be like this:
var color = UIColor(white: 1.0, alpha: 1.0)
// ... and then, later ...
color = UIColor(white: 1.0, alpha: 0.0)
The second problem is that your layer has no size. You need to give it one:
var maskLayer = CALayer()
maskLayer.frame = CGRectMake(
0, 0, self.imageView.bounds.width, self.imageView.bounds.height)
The third and biggest problem is that your mask image is never getting into your mask layer, because you have forgotten to extract its CGImage:
maskLayer.contents = maskImage.CGImage
That last one is really the killer, because if you set the contents to a UIImage without extracting its CGImage, the image fails silently to get into the layer. There is no error message, no crash - and no image.
Making those three corrections in your code, I was able to make the mask punch a rectangular hole in an image. So if that's your purpose, those changes will achieve it.

Related

swift ImageView with low alpha and subImageView transparent showing parent view but with 1 alpha

I'm having trouble setting my subImageViews to show parent ImageView with alpha 1 in their area, but all around I need to have alpha 0.25. Do you have any ideas?
I've tried to do it through View, now ImageView but can't find a solution.
16 squares in the middle need to show a clear image, after pressing the button "Start" they would crop their area and send it to other VC.
Thanks for help
So I've been playing around a bit and got solution (also thanks to #iOSDev link to article).
If anyone will need similar kind of thing, here it is :
#IBOutlet weak var alphaView: UIView!
#IBOutlet weak var pickedImage: UIImageView!
#IBOutlet weak var bigStackView: UIStackView!
override func viewWillAppear(_ animated: Bool) {
pickedImage.image = imagePicked
// Set white background color with custom alpha
alphaView.backgroundColor = UIColor(white: 255/255, alpha: 0.85)
// Create the initial layer from the stackView bounds.
let maskLayer = CAShapeLayer()
maskLayer.frame = alphaView.bounds
// Create the frame to cover whole stackView
let rect = CGRect(
x: bigStackView.frame.minX,
y: bigStackView.frame.minY,
width: bigStackView.bounds.width,
height: bigStackView.bounds.height)
// Create the path
let path = UIBezierPath(rect: alphaView.bounds)
maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
// Append the framer to the path so that it is subtracted
path.append(UIBezierPath(rect: rect))
maskLayer.path = path.cgPath
// Set the mask of the alphaview
alphaView.layer.mask = maskLayer
}
pickedImage is background image view which is covered by aplhaView used only to make it foggy and bigStackView is 16 square in middle to determinate position of wanted square. Might aswell use explicit CGPoint & CGSize Value.
Screenshot from simulator

How to merge 2 pdf vector image assets in Swift

I have two vector images. I want to merge them so the dots appear on top of the first image.
The solutions for regular png assets don't work, as they use the actual size of the image, and the combined image comes out very blurry.
One solution that I found was placing a second UIImageView (same size, centred) on top of the first one, but it seems really dumb.
I need to make this inside the app because I also want to have the squared coloured differently (change the tint of the vector), and then merged with the black border image. So I can have 3 assets in my project (square, border and dots).
Figured out a way to do this in an extension to UIImageView. Surprised that I couldn't find this anywhere.
extension UIImageView {
func mergeTwoPDF(one: UIImage, two: UIImage) {
UIGraphicsBeginImageContextWithOptions(self.frame.size, false, UIScreen.main.scale)
let areaSize = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
one.draw(in: areaSize, blendMode: .normal, alpha: 1.0)
two.draw(in: areaSize, blendMode: .normal, alpha: 1.0)
//If you want to merge more than 2 images, just add them to the func parameters and repeat the line above with them
let mergedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
self.image = mergedImage
}
}
I suggest using sublayers:
["firstOne", "secondOne"].forEach {
let myLayer = CALayer()
let myImage = UIImage(named: $0)?.cgImage
myLayer.frame = myView.bounds
myLayer.contents = myImage
myView.layer.addSublayer(myLayer)
}
Or create a custom layer class

Create new UIImage from freehand drawn line over UIImage - Swift

This is my first question, so please excuse me if I'm unclear in any way.
I would like to be able to create a new UIImage2 from the contents of a freehand drawn shape over an existing UIImage1 to display over the existing UIImage1.
A visual example: UIImage1 is a photo of a city skyline. The user should be able to draw an outline around a skyscraper and create a new UIImage2 which will be displayed stacked over UIImage1, with the ability to edit (size, color, etc) of UIImage 2.
Drawing lines over a UIImage isn't too difficult, but I've yet to figure out how to capture the content from the UIImage within that freehand drawn shape creating a separate UIImage.
I'd greatly appreciate any help.
If you want to create an image from a masked path, you can:
Create UIBezierPath from user gesture;
Use that bezier path as the CAShapeLayer on the image view;
When the user gesture is done, remove that shape layer, but create new shape layer and use it as a mask; and
Create image from that masked image view using UIGraphicsImageRenderer and drawHierarchy to render whatever should be captured in the image.
For example:
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
private var path: UIBezierPath?
private var strokeLayer: CAShapeLayer?
#IBAction func didHandlePan(_ gesture: UIPanGestureRecognizer) {
let location = gesture.location(in: imageView)
switch gesture.state {
case .began:
path = UIBezierPath()
path?.move(to: location)
strokeLayer = CAShapeLayer()
imageView.layer.addSublayer(strokeLayer!)
strokeLayer?.strokeColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1).cgColor
strokeLayer?.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
strokeLayer?.lineWidth = 5
strokeLayer?.path = path?.cgPath
case .changed:
path?.addLine(to: location)
strokeLayer?.path = path?.cgPath
case .cancelled, .ended:
// remove stroke from image view
strokeLayer?.removeFromSuperlayer()
strokeLayer = nil
// mask the image view
let mask = CAShapeLayer()
mask.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
mask.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
mask.lineWidth = 0
mask.path = path?.cgPath
imageView.layer.mask = mask
// get cropped image
let image = imageView?.snapshot
imageView.layer.mask = nil
// perhaps use that image?
imageView.image = image
default: break
}
}
}
By the way, to create UIImage from a view (masked or otherwise), you can use:
extension UIView {
var snapshot: UIImage {
UIGraphicsImageRenderer(bounds: bounds).image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
}
That yields something like:

Using an UIView as a Mask in another UIView on Swift

I am developing an App on xCode with Swift. My screen has an Image of an animal (outlet "backImage"), over this image I have a view (outlet "coverView"), color black, which covers all the screen. So so far you can't see the animal image, because the "coverView" is in front of it. Then I have another view (outlet "maskView") which is smaller and it's over the big view "coverView". What I want is to use this "maskView" as mask and therefor see the "backImage" through it, like a window.
Is there anyone out there able to figure this out?
Here is my screen, I want to see the woman character behind the big gray view through the smaller white view:
You can set the alpha property from your mask view and add in front of the other view, for instance:
let maskView = UIView()
maskView.backgroundColor = UIColor(white: 0, alpha: 0.5) //you can modify this to whatever you need
maskView.frame = CGRect(x: 0, y: 0, width: imageView.frame.width, height: imageView.frame.height)
yourView.addSubview(maskView)
EDIT: Now that you edited your question with an image, now I see what you need, so here is how you can accomplish that.
func setMask(with hole: CGRect, in view: UIView){
// Create a mutable path and add a rectangle that will be h
let mutablePath = CGMutablePath()
mutablePath.addRect(view.bounds)
mutablePath.addRect(hole)
// Create a shape layer and cut out the intersection
let mask = CAShapeLayer()
mask.path = mutablePath
mask.fillRule = kCAFillRuleEvenOdd
// Add the mask to the view
view.layer.mask = mask
}
With this function, all you need is to have a view and create a shape that it's going to be a hole in that view, for instance:
// Create the view (you can also use a view created in the storyboard)
let newView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height))
newView.backgroundColor = UIColor(white: 0, alpha: 1)
// You can play with these values and find one that fills your need
let rectangularHole = CGRect(x: view.bounds.width*0.3, y: view.bounds.height*0.3, width: view.bounds.width*0.5, height: view.bounds.height*0.5)
// Set the mask in the created view
setMask(with: rectangularHole, in: newView)
Thank you, #Alexandre Lara! You did it!
Here goes the solution:
#IBOutlet weak var windowView: UIView!
#IBOutlet weak var bigCoverView: UIView!
func setMask(with hole: CGRect, in view: UIView){
// Create a mutable path and add a rectangle that will be h
let mutablePath = CGMutablePath()
mutablePath.addRect(view.bounds)
mutablePath.addRect(hole)
// Create a shape layer and cut out the intersection
let mask = CAShapeLayer()
mask.path = mutablePath
mask.fillRule = kCAFillRuleEvenOdd
// Add the mask to the view
view.layer.mask = mask
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let rectangularHole = windowView.frame.integral
// Set the mask in the created view
setMask(with: rectangularHole, in: bigCoverView!)
}

Change UIImage color without using Uiimageview in swift3

I want draw image pattern like star, one can change the color for that. Can I change the color of Image itself without using image view? I saw many answer changing tint color of UIImageview, but the effect is not applied to UIImage
Thank you all for the answers, but I got the solution working.
func changePatternImageColor() {
patternImage = UIImage(named: "pattern_star.png")! //make sure to reinitialize the original image here every time you change the color
UIGraphicsBeginImageContext(patternImage.size)
let context = UIGraphicsGetCurrentContext()
let color = UIColor(red: self.red, green: self.green, blue: self.blue, alpha: self.opacity)
color.setFill()
context?.translateBy(x: 0, y: patternImage.size.height)
context?.scaleBy(x: 1.0, y: -1.0)
//set the blend mode to color burn, and the original image
context?.setBlendMode(CGBlendMode.exclusion) //this does the magic.
let rect = CGRect(x: 0, y: 0, width: patternImage.size.height, height: patternImage.size.height)
context?.draw(patternImage.cgImage!, in: rect)
//set the mask that matches the shape of the image, then draw color burn a colored rectangle
context?.clip(to: rect, mask: patternImage.cgImage!)
context?.addRect(rect)
context?.drawPath(using: CGPathDrawingMode.fill)
patternImage = UIGraphicsGetImageFromCurrentImageContext()!
image_ref = patternImage.cgImage
UIGraphicsEndImageContext()
}
The patternImage is the the image added in asset.
P.S. the patternImage added in asset SHOULD be in black color
tintColor is not only a property of a UIImageView, it is a property of a UIView, as long as your image is in a UIView, you can use UIView.tintColor.