Add shadow to image in UIKit - ios

I have this screen with an UIImageView (the image is a SF Symbol)
The user can see this icon clearly, white on black. But - if the background is different:
It may be hard to see it.
I want to add a black shadow to the image so the user can see it better.
I tried looking online but all I saw is how to add shadow to the image view box, and that is not what I need. I need the shadow around the icon itself and not around the box of the image.
Any ideas? thanks in advance

Try this:
imageView.layer.shadowColor = UIColor.black.cgColor
imageView.layer.shadowRadius = 3.0
imageView.layer.shadowOpacity = 1.0
imageView.layer.shadowOffset = CGSize(width: 4, height: 4)
imageView.layer.masksToBounds = false

Related

iOS : how to use an asset with a shadow as a button image and make the shadow part not tappable?

I have an asset representing a button with a shadow below. I want to make only the blue part tappable. Is there an easy way to do so?
Thanks.
You can add a drop shadow programmatically like this:
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOffset = CGSize(width: 5.0, height: 5.0)
button.layer.shadowRadius = 3.0
button.layer.shadowOpacity = 1.0
so, swap out a version of the button asset that doesn't have the built-in shadow, and add the shadow as above. The new shadow will not change the tappable area of the button, no matter the size/offset.

What do I need for masking a UIImageView and how do I do it in Swift 3?

I was wondering what I would need if I wanted to use a mask image to get my UIImageView in a specific shape. From what I understand, to create a mask, I need to have an image with the shape of the mask all black on top of a white background. Something like this, for example:
First of all, is this sufficient to shape an image view, and if so, how do I do it in Swift 3? I can only find masking code that is either outdated or written in Objective-C. I've tried simply assigning the image above to an UIImageView and then assign the image view to the mask property of the UIImageView I want to shape, like so:
self.defaultImageView.mask = self.maskImageView
This didn't do anything. It just made self.maskImageView disappear (both image view's added through the storyboard and connected using IBOutlet properties). I'm sure I'm forgetting to do something. It can't be this simple. I would appreciate it if someone could help me out. Like I said, I put both image views on the exact same spot, on top of each other, in the storyboard.
UPDATE:
My first attempt to set the mask programmatically after deleting it from my storyboard.
let layer:CALayer = CALayer()
let mask:UIImage = UIImage(named: "Black-Star-Photographic-Agency")!
layer.contents = mask
layer.frame = CGRect(x: 0, y: 0, width: ((self.defaultImageView.image?.size.width)!), height: (self.defaultImageView.image?.size.height)!)
self.defaultImageView.layer.mask = layer
self.defaultImageView.layer.masksToBounds = true
The result was that the image view had completely disappeared and wasn't visible anymore. Am I doing something, am I forgetting something or both?
You should use a png image, which supports transparency, unlike jpg.
In Photoshop your image should look similar to this:
It doesn't matter if your shape is black or white. What matters is transparency of each pixel. Opaque area (black in this case) will be visible and transparent area will get trimmed.
Edit:
You should not create mask view from storyboard if you do so. It is not going to be a part of your view hierarchy. Just add it programmatically like this:
let maskView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
maskView.image = UIImage(named: "mask")
imageView.mask = maskView
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
maskView.frame = imageView.bounds
}
Output:
Here is a test project to show how it's working.
Also if you're using a custom frame/image and run into the mask not showing properly, try setting the content mode of the mask:
maskView.contentMode = .scaleAspectFit

Create shadow around a UIVisualEffectView without covering the whole view

Is it possible to create a shadow around a UIVisualView with UIBlurEffect without letting the UIVisualView get coloured by the shadow underneath?
I basically just want the shadow around the view but with this code the shadow will cover the whole view which darkens the whole view to much:
let borderPath = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 15, height: 15)).cgPath
shadowView.frame = view.bounds
shadowView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
shadowView.layer.shadowOpacity = 0.3
shadowView.layer.shadowRadius = 3.0
shadowView.backgroundColor = UIColor.clear
shadowView.layer.shadowPath = borderPath
shadowView.layer.shadowOffset = CGSize(width: 0, height: 0)
self.view.insertSubview(shadowView, at: 0)
let blurEffect = UIBlurEffect(style: .extraLight)
let blurView = UIVisualEffectView(effect: blurEffect)
blurView.frame = view.bounds
blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
blurView.clipsToBounds = true
blurView.layer.cornerRadius = 15
view.insertSubview(blurView, aboveSubview: shadowView)
EDIT.
I need to achieve the same thing as in Apple's Maps application. Where the draggable favourite view both uses the UIVisualEffectView and a shadow around its top, without interfering with the UIVisualEffectView's background.
See example screenshots:
Ok, so the problem was that my background in the underlying view was white. And with the UIBlurEffect .extraLight used on a background which is lighter than the BlurEffect the shadow beneath a UIVisualView appears darker than with a more vivid background.
Also described in this question:
Fix UIVisualEffectView extra light blur being gray on white background
UPDATE
I found a project explaining how to solve this on Github The solution involves creating a 9-part UIImage to represent the shadow. The creator also explains the underlying layers of the iOS 10 Maps.
So i'm trying to recreate the look of iOS 10 maps. I decided to attach the maps app in the simulator to the debugger to see what was going on...
Apple actually get around this by having a UIImage over the top of the content with the border and shadow. Not the most elegant way to do it but i'm going for the exact look so I'm going to take the exact approach.
I also grabbed the asset (using this) from the Maps app to save making your own one. Shame they only have #2x artwork in it though :/
I found in iOS 12, this is not a problem, the UIVisualEffectView ignore the shadow of underneath views, it just sees through the shadow, like the shadow not exist.
The trick is to not set the shadowPath of the layer. If it is set, the shadow is painted below the visual effect view and, in consequence, darkens the blurred view.
The documentation for shadowPath states:
If you specify a value for this property, the layer creates its shadow
using the specified path instead of the layer’s composited alpha
channel.
The "instead…" part is what we actually need: the shadow should be composed after rendering the content, based on what the content leaves transparent. Now you might be deterred from not setting a shadowPath because the documentation also states that an "explicit path usually improves rendering performance". However, I didn't see any issues in real life so far. In comparison to the rendering cost of the UIVisualEffectView, painting the shadow doesn't make a difference, I assume.
Also make sure to set the shadow on a superview of the UIVisualEffectView, not on a sibling view.
I usually solve this kind of situation using composition of views. Instead of setting the shadow in the target view, I create a ShadowView and I put it behind the target view, both views with the same frame.
I've posted and example and code in this question.
An example of the result of this approach is the following:

Sprite color not changing in Swift iOS

My sprite image is black, I want to change this color to whatever I want.
The code I'm using:
var sprite = SKSpriteNode(imageNamed: "layer1-0\(random(1..<4))")
ceilingPieces.append(sprite)
sprite.setScale(0.5)
sprite.position = CGPointMake(900, 335)
sprite.color = UIColor.purpleColor()
sprite.colorBlendFactor = 1.0
self.addChild(sprite)
The image stays black, but I want it to be purple.
Thanks!
Toby.
A black tile image isn't going to be able to tint. You're working with filters here so it doesn't matter what you put over it, it's still going to look black. Get a white or mostly white image and you'll see that it will work as expected. Again, tint color is multiplied by the original color to get to the end result.
If you were instead working with
var sprite = SKSpriteNode(color: UIColor.blackColor(), size: CGSizeMake(100, 100))
you could then change the color but it would be a blend, it would simply be changing the color with
sprite.color = UIColor.purpleColor()

Shadow lagging the user interface

I have around 15 views for which I apply QuartzCore shadow like this:
for button in buttonsArray {
button.layer.shadowOpacity = 0.75
button.layer.shadowColor = UIColor.blackColor().CGColor
button.layer.shadowRadius = 2.0
button.layer.shadowOffset = CGSize(width: 0.4, height: 1.2)
}
When I have a lot of this shadow in 1 view(like in this example) it really starts to slow and lag the user interface.
How can I fix it, or what other alternatives do I have? Thanks!
Check out the WWDC 2014 video 'Advanced Graphics and Animations for iOS Apps'.
https://developer.apple.com/videos/wwdc/2014/
35:50 in ... it talks about why using that type of code to generate a shadow causes extra off screen passes for the GPU as it figures out the shape of the shadow.
The suggested solution is to also use the shadowPath property on the layer if you already know the shape of the shadow...
It's a great video to watch.
Depending on how your buttons look, one idea would be to use a background image for the buttons that includes the shadow, instead of adding the shadow in code. You can also use resizableImageWithCapInsets to make for the various possible sizes of your buttons.
UIImage* image = [UIImage imageNamed:#"button.png"];
UIEdgeInsets insets = UIEdgeInsetsMake(16, 6, 16, 6); // change the insets to fit your needs
image = [image resizableImageWithCapInsets:insets];
[button setBackgroundImage:image forState:UIControlStateNormal];
Documentation on resizableImageWithCapInsets can be found at https://developer.apple.com/library/ios/documentation/uikit/reference/uiimage_class/index.html#//apple_ref/occ/instm/UIImage/resizableImageWithCapInsets:
This solution may not be fit for you, depending on how your buttons look. But using images instead of adding the shadow from code should speed up your code.

Resources