Flat User interfaces are a lie. They are not completely flat. One effect continues to be used with them to show depth: Shadows. In flat interfaces it can look like there are some views floating above others by the use of shadows. In this lesson we’ll learn to make shadows using the CALayer
found in UIViews,
and use them to make some special effects on UIButtons.
Make a new single-view project named SwiftShadowDemo with a Swift as the Language and Universal device. Go to the storyboard. Drag a button on to the storyboard. Give it a Blue color for a background and white lettering. Title it Shadow.
With the button selected, Control-Drag Up from the button and to the left, until your cursor is on the white of the background scene. Release the button. You will get a menu. Shift select the items Center Horizontally to Container, Center vertically to Container, Equal Widths and Equal Heights like this:
You’ve probably not set the button in the exact center of the scene. Go to the the size inspector and find the constraints. Select Edit on the Align X to Shadow constraint. Change the constant to 0 .
Do the same for the Align Center Y to Shadow. When done both constraints should read like this
Now edit the Equal Width to Superview. Change the multiplier to 3 or 1:3 depending which gives you a smaller rectangle.
Do the same to Equal Height to Superview, with a multiplier of 3 or 1:3. Next in the resolver menu, Update the frames
This should give you a centered proportionally sized button:
Open the assistant editor. Control-drag from the button to the code. Make an outlet named shadowButton.
@IBOutlet weak var shadowButton: UIButton!
Control drag again from the button to the code. Make a action named shadowButton.
@IBAction func shadowButton(sender: AnyObject) { }
Making Shadows on the CALayer
All UIViews
have a special property layer of type CALayer.
This is a Core Animation layer. As it name implies various animation effects to your views happen here. The layer property is great for many quick special effects, some built-in. Drop shadows are one of those built-in effects. Add the following to viewDidLoad.
shadowButton.layer.shadowOpacity = 0.7
The property shadowOpacity
defaults to 0. The shadow is always there, but hidden. To show a shadow, change the opacity. Build and run. We have a shadow:
This shadow’s light source is from the lower right. Most times it’s best to have a light source from the top and left, so the shadow shows on the lower right. We change that by the property of CALayer
. Add this to viewDidLoad:
shadowButton.layer.shadowOffset = CGSize(width: 10.0, height: 10.0)
Build and run and you get this:
We have a nice bold shadow. Usually shadows are subtle. Change the shadowOffset
to this:
shadowButton.layer.shadowOffset = CGSize(width: 3.0, height: 2.0)
Build and run. The shadow is more subtle:
We can also change the radius of the shadow. This affects how blurry the shadow appears. Add this to viewDidLoad
shadowButton.layer.shadowRadius = 5.0
Build and run:
We have a more blurred shadow. We can also change the color of the shadow. The default color is black. Let’s change the color to yellow. Add this:
shadowButton.layer.shadowColor = UIColor.yellowColor().CGColor
Build and run. Rhe shadow now look like more of a glow.
Glows are more radiant from the center. For a good glow effect change the code to the following:
shadowButton.layer.shadowOpacity = 0.7 shadowButton.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) shadowButton.layer.shadowRadius = 15.0 shadowButton.layer.shadowColor = UIColor.yellowColor().CGColor view.backgroundColor = UIColor.grayColor()
To better see the glow we made the background gray to contrast against the glow. Build and run:
We get a glow behind the button.
While this is nice for a static shadow, glows and shadows make for dynamic effects on a button. To the shadow button action, add the following code:
@IBAction func shadowButton(sender: AnyObject) { shadowButton.layer.shadowOpacity = 0.7 shadowButton.layer.shadowOffset = CGSize(width: 3.0, height: 2.0) shadowButton.layer.shadowRadius = 5.0 shadowButton.layer.shadowColor = UIColor.blackColor().CGColor }
This will make our simple drop shadow when we release the button. When we press the button, we will give one of two shadow effects, depending on the selection on a segmented control. Add this outlet and action to your code:
@IBOutlet weak var touchDownType: UISegmentedControl! @IBAction func shadowButtonTouchDown (sender: AnyObject) { // code to change the button on pressing if touchDownType.selectedSegmentIndex == 0{ //make the button glow shadowButton.layer.shadowOpacity = 0.5 shadowButton.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) shadowButton.layer.shadowColor = UIColor.yellowColor().CGColor shadowButton.layer.shadowRadius = 20.0 } else { //make the button depress by having a shadow in the upper left shadowButton.layer.shadowOpacity = 0.9 shadowButton.layer.shadowOffset = CGSize(width: -3.0, height: -2.0) shadowButton.layer.shadowColor = UIColor.blackColor().CGColor shadowButton.layer.shadowRadius = 2.0 } }
Go to the storyboard. Add a segmented control above the button. Control drag directly down from the segmented control to the button. In the auto layout menu that appears, Shift-select Vertical Spacing, Center X, and Equal Widths
As we did earlier with the button, we will change some constraints. With the segmented control selected, go to the size inspector. Change the Bottom Space to Shadow constraint to 20. Change Align Center X to Shadow to 0. Your constraints should read like this:
Change the first segment title to Glow. Change the second segment title to Depression. Select the button. In the size inspector, change the Proportional width to Superview’s Multiplier to 1.5 if set it at 3. Set the multiplier to 1:1.5 if set at 1:3.
Your scene should look like this:
Open the assistant editor. From the circle next to the touchDownType
outlet, drag to the storyboard to connect it to the segment.
For events other than a touch up for a button we need to explicitly set the event. For our new action, we need to set an event for touching down on the button. Right click the button in the storyboard. In the menu that appears, find Touch Down. Drag from the circle to the right up to anywhere in the shadowButtonTouchDown
method’s code in the assistant editor. When the code highlights, release the button.
We have wired our storyboard up. Now to set our default. Change viewDidLoad
to this:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. shadowButton.layer.shadowOpacity = 0.5 shadowButton.layer.shadowOffset = CGSize(width: 3.0, height: 2.0) shadowButton.layer.shadowRadius = 5.0 shadowButton.layer.shadowColor = UIColor.blackColor().CGColor shadowButton.setTitleColor(UIColor.yellowColor(), forState: .Highlighted) //need to show button title view.backgroundColor = UIColor(red: 0.25, green: 0.2, blue: 0.6, alpha: 1.0) }
We set up our basic shadow, like we did in the touch up event. We also added two lines of code to makes things a bit more visible. First we changed the color of the button’s title. If we leave it as the white from the storyboard, it will disappear. Changing the color to yellow fixes this. Secondly, we gave a contrasting color to both our glow and shadow so they are visible.
Build and run. Rotate the device in the simulator with Command-Right arrow. You should see this:
Press the button, and the button should glow:
Switch to Depression
Press the button and the button seems to press down:
This is some of the basics of drop shadows. You can do this to any UIView
based control. For example
you can do this to the segment and a label, which we’ll leave to the reader to set up to produce the image below:
//a shadow on the segmented control and on a label //label layout and setup is left to the reader. let offset = CGSize(width: 3.0, height: 2.0) //keep offsets consistent -- usually make this a constant. touchDownType.layer.shadowOpacity = 0.7 touchDownType.layer.shadowOffset = offset shadowLabel.layer.shadowOffset = offset shadowLabel.layer.shadowOpacity = 0.9 shadowLabel.layer.shadowRadius = 7.0
The Whole Code
// // ViewController.swift // SwiftShadowDemo // // Created by Steven Lipton on 5/15/15. // Copyright (c) 2015 MakeAppPie.Com. All rights reserved. // import UIKit class ViewController: UIViewController { //MAARK: - Outlets @IBOutlet weak var shadowLabel: UILabel! @IBOutlet weak var shadowButton: UIButton! @IBOutlet weak var touchDownType: UISegmentedControl! //MARK: - Actions @IBAction func shadowButtonTouchDown (sender: AnyObject) { //When button is pressed down if touchDownType.selectedSegmentIndex == 0{ shadowButton.layer.shadowOpacity = 0.5 shadowButton.layer.shadowOffset = CGSize(width: 0.0, height: 0.0) shadowButton.layer.shadowColor = UIColor.yellowColor().CGColor shadowButton.layer.shadowRadius = 20.0 } else { shadowButton.layer.shadowOpacity = 0.9 shadowButton.layer.shadowOffset = CGSize(width: -3.0, height: -2.0) shadowButton.layer.shadowColor = UIColor.blackColor().CGColor shadowButton.layer.shadowRadius = 2.0 } } @IBAction func shadowButton(sender: AnyObject) { //When button released shadowButton.layer.shadowOpacity = 0.5 shadowButton.layer.shadowOffset = CGSize(width: 3.0, height: 2.0) shadowButton.layer.shadowRadius = 5.0 shadowButton.layer.shadowColor = UIColor.blackColor().CGColor } //MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. shadowButton.layer.shadowOpacity = 0.5 shadowButton.layer.shadowOffset = CGSize(width: 3.0, height: 2.0) shadowButton.layer.shadowRadius = 5.0 shadowButton.layer.shadowColor = UIColor.blackColor().CGColor shadowButton.setTitleColor(UIColor.yellowColor(), forState: .Highlighted) //need to show button title view.backgroundColor = UIColor(red: 0.25, green: 0.2, blue: 0.6, alpha: 1.0) //extra code not in post - a shadow on the segmented control and on a label let offset = CGSize(width: 3.0, height: 2.0) //keep offsets consistent -- usually make this a constant. touchDownType.layer.shadowOpacity = 0.7 touchDownType.layer.shadowOffset = offset shadowLabel.layer.shadowOffset = offset shadowLabel.layer.shadowOpacity = 0.9 shadowLabel.layer.shadowRadius = 7.0 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
Leave a Reply