How to make a drop shadow effect on a label in Swift? - ios

I can't figure out how to code a drop shadow on a label. I have a score label that changes so just photoshopping text with shadows wont be possible. I need to code it so it automatically has a blurry shadow behind the text at all times. Can anyone come with some examples or help?
People saying this is a duplicate, the "duplicate" is about drop shadows on UIView, mine is about UILabel. It's not the same thing.

Give this a try - you can run it directly in a Playground page:
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 400))
container.backgroundColor = UIColor.lightGray
PlaygroundPage.current.liveView = container
var r = CGRect(x: 40, y: 40, width: 300, height: 60)
let label = UILabel(frame: r)
label.font = UIFont.systemFont(ofSize: 44.0)
label.textColor = .white
label.frame = r
label.text = "Hello Blur"
container.addSubview(label)
label.layer.shadowColor = UIColor.black.cgColor
label.layer.shadowRadius = 3.0
label.layer.shadowOpacity = 1.0
label.layer.shadowOffset = CGSize(width: 4, height: 4)
label.layer.masksToBounds = false
Play around with different values for the shadow Color, Opacity, Radius and Offset
Result:

UILabel has a property for changing its shadow, the image below shows the property in attributes inspector and the result.
Result of that effect on label

You can write an extension and use it. Place the extension code outside of class ViewController.
I like subtle shadow.
extension UILabel {
func textDropShadow() {
self.layer.masksToBounds = false
self.layer.shadowRadius = 2.0
self.layer.shadowOpacity = 0.2
self.layer.shadowOffset = CGSize(width: 1, height: 2)
}
static func createCustomLabel() -> UILabel {
let label = UILabel()
label.textDropShadow()
return label
}
}
On your label simply call this method
myLabel.textDropShadow()

works fine but add shadow to ALL label, not to text.
in this case:
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let shadow = NSShadow()
shadow.shadowColor = UIColor.blue
shadow.shadowBlurRadius = 10
let attrs: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: 36),
.foregroundColor: UIColor.red,
.shadow: shadow
]
let s = "MY TEXT"
let attributedText = NSAttributedString(string: s, attributes: attrs)
self.label.attributedText = attributedText
}
}
You will get:
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/CRMpg.png
**note:** You must add attributed string every time, as shadow is an attribute of string, not label, otherwise you can also derive class and override "setText". (keeping attributes inside the object in a a property you can set on init/setter)

Swift 4, IBInspectable using extension
extension UILabel {
#IBInspectable var isShadowOnText: Bool {
get {
return self.isShadowOnText
}
set {
guard (newValue as? Bool) != nil else {
return
}
if newValue == true{
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowRadius = 2.0
self.layer.shadowOpacity = 1.0
self.layer.shadowOffset = CGSize(width: 2, height: 2)
self.layer.masksToBounds = false
}
}
}
}

Swift 4 - Extension with shadow parameters:
// Label Shadow
extension UILabel {
func lblShadow(color: UIColor , radius: CGFloat, opacity: Float){
self.textColor = color
self.layer.masksToBounds = false
self.layer.shadowRadius = radius
self.layer.shadowOpacity = opacity
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
On your label simply call this method
let titleColor = UIColor(red:0.08, green:0.08, blue:0.08, alpha:1.0)
titleLbl.lblShadow(color: titleColor, radius: 3, opacity: 0.25)

U can make a extension method for all UIView subclasses.
extension UIView {
func drawShadow(offset: CGSize, opacity: Float = 0.25, color: UIColor = .black, radius: CGFloat = 1) {
layer.masksToBounds = false
layer.shadowColor = color.cgColor
layer.shadowOffset = offset
layer.shadowOpacity = opacity
layer.shadowRadius = radius
}
}

Related

UITextField seems to ignore clipsToBounds property

I am creating a custom search bar by subclassing the UITextField. For some reason, the search bar has a very subtle, weird white line/border around the edges. The issue only exists on my iPhone 13 Pro Max - on the simulator everything works perfectly fine.
notice the slight white border around the pill shaped search text field. Almost like it has a white background color.
I tried to battle this by setting layer.masksToBounds and clipsToBounds to true, but unfortunately that doesn't work either.
This is my code:
public class MyCustomSearchBar: UITextField {
public override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = Theme.GRAY800
self.borderStyle = .roundedRect
self.layer.masksToBounds = true
self.clipsToBounds = true
self.autocorrectionType = .no
self.textColor = .white
self.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [.foregroundColor : Theme.GRAY400, .font: FontFamily.ProximaNova.regular.font(size: 16)])
}
}
UPDATE:
If I add a border width and color, everything looks normal again:
Question:
Why is this a problem on my iPhone but not in the simulator? Why do I manually have to set the border width and color to avoid those white "fragments"?
create border and corner manually, this is an example:
let myTextField = MyCustomSearchBar()
in viewDidLoad set constraints
view.addSubview(myTextField)
myTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
myTextField.widthAnchor.constraint(equalToConstant: view.frame.width - 40).isActive = true
myTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myTextField.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
now assign border width, border color e round corner in MyCustomSearchBar class
public class MyCustomSearchBar: UITextField {
public override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .yourColor
self.borderStyle = .none
self.layer.masksToBounds = true
self.layer.cornerRadius = 16
self.clipsToBounds = true
self.layer.borderWidth = 2
self.layer.borderColor = UIColor.white.cgColor
self.autocorrectionType = .no
self.textColor = .white
self.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [.foregroundColor : UIColor.lightGray, .font: UIFont.systemFont(ofSize: 16, weight: .regular)])
self.addPadding(padding: .left(20)) // this is a extra
self.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
to add padding spacing to your textField use my extension:
extension UITextField {
enum PaddingSpace {
case left(CGFloat)
case right(CGFloat)
case equalSpacing(CGFloat)
}
func addPadding(padding: PaddingSpace) {
self.leftViewMode = .always
self.layer.masksToBounds = true
switch padding {
case .left(let spacing):
let leftPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
self.leftView = leftPaddingView
self.leftViewMode = .always
case .right(let spacing):
let rightPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
self.rightView = rightPaddingView
self.rightViewMode = .always
case .equalSpacing(let spacing):
let equalPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: spacing, height: self.frame.height))
// left
self.leftView = equalPaddingView
self.leftViewMode = .always
// right
self.rightView = equalPaddingView
self.rightViewMode = .always
}
}
}
This is the result:

Swift & UILabel : How to add padding and margin in Swift programmatically? [duplicate]

This question already has answers here:
Add padding between label and its border
(4 answers)
Closed 8 months ago.
I have created a text programmatically with a grey background using UILabel.
Now I would like to add padding to this paragraph/text. Also, it would be great if you could show me how to add margin to my UILabel as well.
import UIKit
final class SignUpViewController: UIViewController {
public let identifier = "Sign Up"
private let logoImage : UIImageView = {
let imageView = UIImageView()
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "MyLogoWithTitle")
imageView.clipsToBounds = true
return imageView
}()
private let instructionText : UILabel = {
let label = UILabel()
label.text = "Please read terms and conditions below carefully before proceeding with the registration."
label.backgroundColor = UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4)
label.numberOfLines = 0
label.tintColor = .white
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addSubview(logoImage)
view.addSubview(instructionText)
view.backgroundColor = UIColor().colorFromHex(hex: "#141920", opacity: 1.0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
logoImage.frame = CGRect(x: 0,
y: 0,
width: 140,
height: 60)
logoImage.center = CGPoint(x: view.center.x, y: view.height/5)
instructionText.frame = CGRect(
x: 5,
y: 5 + logoImage.bottom,
width: view.width - 20,
height: 50)
.integral
instructionText.layer.cornerRadius = 10
}
}
Notice that I created an extension to UIColor so that I can input hex color in this way - UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4) .
I am looking forward to hearing from you. Thank you.
You can insert this UILabel into the container (any UIView) and set its position inside.
But the simplest trick is to use UIButton instead of UILabel. You can configure UIEdgeInsets for padding.
So that UIButton does not act as a button simply set button.isUserInteractionEnabled = false.
By default, text in the button are placed in the center, but its position is easy to change with contentHorizontalAlignment and contentVerticalAlignment
And as a bonus, you can add icons right near to the text. :)
UPD.
Could you give me a simple example? I tried that way but I didn't get the result I expected. – Punreach Rany
let buttonUsedAsLabel = UIButton()
// Your question was about padding
// It's it!
buttonUsedAsLabel.titleEdgeInsets = UIEdgeInsets(top: 5, left: 20, bottom: 5, right: 20)
// Make it not user interactable as UILabel is
buttonUsedAsLabel.isUserInteractionEnabled = false
// set any other properties
buttonUsedAsLabel.setTitleColor(.white, for: .normal)
buttonUsedAsLabel.contentVerticalAlignment = .top
buttonUsedAsLabel.contentHorizontalAlignment = .left
// Set title propeties AFTER it was created with text because it's nullable
// You can use attributed title also
// Never use any (button.titleLabel) before its creation
// for example: (button.titleLabel?.text = "zzz") do nothing here
buttonUsedAsLabel.setTitle("This is the text", for: .normal)
buttonUsedAsLabel.titleLabel?.font = .systemFont(ofSize: 20, weight: .medium)
buttonUsedAsLabel.titleLabel?.numberOfLines = 0
buttonUsedAsLabel.titleLabel?.lineBreakMode = .byWordWrapping
// and so on
// ...
// This is the triсk :)
Of course, you can do it with a storyboard if prefer.
1. Add this class
PaddingLabel.swift
import UIKit
class PaddingLabel: UILabel {
var edgeInset: UIEdgeInsets = .zero
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets.init(top: edgeInset.top, left: edgeInset.left, bottom: edgeInset.bottom, right: edgeInset.right)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + edgeInset.left + edgeInset.right, height: size.height + edgeInset.top + edgeInset.bottom)
}
}
2. Add this code to your ViewController
let label = PaddingLabel()
override func viewDidLoad() {
super.viewDidLoad()
label.backgroundColor = UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4)
//Setting the padding label
label.edgeInset = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
}
The answer to the link below is that I wrote the same content based on the storyboard.
Add padding between label and its border
I use textfield. Set padding and text in textfield. And do not allow editing.
extension UITextField {
func addLeftPadding() {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: self.frame.height))
self.leftView = paddingView
self.leftViewMode = ViewMode.always
}
}
//ViewController
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.addLeftPadding()
myTextField.isUserInteractionEnabled = false
myTextField.text = "your label text"
}

Problem with rounded corner in UITableView in Objective C [duplicate]

Hello i have some views with rounded corners, and I'd like to apply a shadow to this views.
SO first I round the view :
view.layer.cornerRadius = 15
view.clipsToBounds = true
then I apply the shadow :
func dropShadow(scale: Bool = true) {
self.layer.masksToBounds = false
self.layer.cornerRadius = 15
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
self.layer.shadowOffset = CGSize(width: 3, height: 3)
self.layer.shadowOpacity = 0.5
self.layer.shadowRadius = 5
}
view.dropShadow()
I got my rounded view with a shadow but the shadow is not rounded like my view. The shadow is not rounded at all
You cannot cast a shadow from a view whose clipsToBounds is true. If a view's masksToBounds is true, its clipsToBounds is true; they are the same thing.
If you want a shadow to appear to come from from a view that clips, you need to use two views: one that the user can see, with rounded corners and clipsToBounds set to true, and another that the user can't see because it's behind the first one, also with rounded corners, but with clipsToBounds set to false, to cast the shadow.
class ShadowView : UIView {
override init(frame: CGRect) {
super.init(frame:frame)
self.isOpaque = true
self.backgroundColor = .black
self.dropShadow()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func dropShadow() {
self.layer.masksToBounds = false
self.layer.cornerRadius = 15
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 3, height: 3)
self.layer.shadowOpacity = 0.5
self.layer.shadowRadius = 5
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let r = CGRect(x: 100, y: 100, width: 100, height: 100)
let v = UIImageView(frame:r)
v.image = UIImage(named:"marsSurface.jpg")
v.clipsToBounds = true
v.backgroundColor = .red
v.layer.cornerRadius = 15
self.view.addSubview(ShadowView(frame:r))
self.view.addSubview(v)
}
}
Note that neither view is a subview of the other, nor do they have a superview that clips, as that would clip the shadow.
Trying to have a single view handle both corner rounding and shadow logic is not recommended. The best way to create the effect you are looking for is to wrap your rounded view with another parent view that owns the shadow.
This can be best illustrated with a playground. Try this code out in a playground.
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let shadowView = UIView()
shadowView.frame = CGRect(x: 50, y: 200, width: 200, height: 100)
shadowView.backgroundColor = nil
shadowView.layer.shadowColor = UIColor.black.cgColor
shadowView.layer.shadowOffset = CGSize(width: 0, height: 4.0)
shadowView.layer.shadowRadius = 8.0
shadowView.layer.shadowOpacity = 1
let roundedView = UIView()
roundedView.frame = shadowView.bounds
roundedView.backgroundColor = .gray
roundedView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner,.layerMaxXMinYCorner]
roundedView.layer.cornerRadius = 10
roundedView.layer.masksToBounds = true
shadowView.addSubview(roundedView)
view.addSubview(shadowView)
view.backgroundColor = UIColor.white
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
Don't forget to set the shadowPath property on your shadow view's layer in either viewWillLayoutSubviews or layoutSubviews. If this property is set to nil, UIKit has to perform off screen rendering to figure out how to draw the shadow rect.

UISearchBar with a white background is impossible?

I really thought it would be easy to set the background color of my UISearchBar's text field to white. But no matter what I try, it always stays offwhite / light gray (#efeff0).
import UIKit
class ViewController: UIViewController {
private let searchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Hello World"
view.backgroundColor = #colorLiteral(red: 0.9588784575, green: 0.9528519511, blue: 0.9350754619, alpha: 1)
searchController.searchBar.searchTextField.backgroundColor = .white
navigationItem.searchController = searchController
}
}
How can I make the search bar have a pure white background color? App is iOS 13+, if that helps.
Tiny test project: https://github.com/kevinrenskers/WhiteSearch.
It's possible. Set the background of the search field with a white image.
let size = CGSize(width: searchController.searchBar.frame.size.width - 12, height: searchController.searchBar.frame.size.height - 12)
let backgroundImage = createWhiteBG(size)!
let imageWithCorner = backgroundImage.createImageWithRoundBorder(cornerRadiuos: 10)!
searchController.searchBar.setSearchFieldBackgroundImage(imageWithCorner, for: UIControl.State.normal)
If you don't want to input an image to app. Try this for create one programmatically.
func createWhiteBG(_ frame : CGSize) -> UIImage? {
var rect = CGRect(x: 0, y: 0, width: 0, height: 0)
rect.size = frame
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(UIColor.white.cgColor)
context?.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
extension UIImage {
func createImageWithRoundBorder(cornerRadiuos : CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.size, false, scale)
let rect = CGRect(origin:CGPoint(x: 0, y: 0), size: self.size)
let context = UIGraphicsGetCurrentContext()
let path = UIBezierPath(
roundedRect: rect,
cornerRadius: cornerRadiuos
)
context?.beginPath()
context?.addPath(path.cgPath)
context?.closePath()
context?.clip()
self.draw(at: CGPoint.zero)
context?.restoreGState()
path.lineWidth = 1.5
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Try this ... Change colors and images according to your preference
DispatchQueue.main.async {
searchBar.backgroundImage = UIImage()
for s in searchBar.subviews[0].subviews {
if s is UITextField {
s.layer.borderWidth = 1.0
s.layer.borderColor = UIColor.lightGray.cgColor
}
}
let searchTextField:UITextField = searchBar.subviews[0].subviews.last as? UITextField ?? UITextField()
searchTextField.layer.cornerRadius = 10
searchTextField.textAlignment = NSTextAlignment.left
let image:UIImage = UIImage(named: "search")!
let imageView:UIImageView = UIImageView.init(image: image)
searchTextField.leftView = nil
searchTextField.placeholder = "Search..."
searchTextField.font = UIFont.textFieldText
searchTextField.rightView = imageView
searchTextField.rightViewMode = UITextField.ViewMode.always
}
Here is My complete Custom Search Bar Which you can define the searchbar backgroundColor and TextField background Color
Tested
import Foundation
class SearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
makeUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
makeUI()
}
private func makeUI( ) {
//SearchBar BackgroundColor
self.backgroundImage = UIImage(color: UIColor.white)
//Border Width
self.layer.borderWidth = 1
//Border Color
self.layer.borderColor = UIColor("DEDEDE")?.cgColor
//Corner Radius
self.layer.cornerRadius = 3
self.layer.masksToBounds = true
//Change Icon
self.setImage(UIImage(named: "search")?
.byResize(to: CGSize(width: 30, height: 30)), for: .search, state: .normal)
if let searchTextField = self.value(forKey: "searchField") as? UISearchTextField {
//TextField Background !!!!!
searchTextField.backgroundColor = UIColor.white
//TextField Font
searchTextField.font = UIFont(name: "Poppins-Regular", size: 21)
searchTextField.textColor = .black
}
}
}

Problem in setting bottom border for UITextField in swift 5

I am new to swift. I have two UITextFields email and password. I want to add only bottom border to text fields and for that I am using the following method,
extension UITextField {
func setBottomBorderWithLayer() {
self.borderStyle = .none
self.layer.masksToBounds = false
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
self.layer.shadowOpacity = 1.0
self.layer.shadowRadius = 0.0
}
}
And this is how I am calling the method
emailTextField.setBottomBorderWithLayer()
passwordTextField.setBottomBorderWithLayer()
Now the problem is setBottomBorderWithLayer() method only setting bottom border for only emailTextField and not for passwordTextField.
Can anyone explain that where is the problem?
Thanks in advance.
We need more details to be able to define where the error is, but I suggest another type of implementation that will meet your need.
Create a class that extends from UITextField, in this class you can define all the necessary layout settings, facilitating reuse, as you will only need to define the class in TextField from Storyboard -> TextField -> Identity Inspector -> Custom Class -> Class.
Class example:
import UIKit
class BottomBorderTextField: UITextField {
override init (frame: CGRect) {
super.init (frame: frame)
addBottomBorder()
}
required init? (coder: NSCoder) {
super.init (coder: coder)
addBottomBorder()
}
func addBottomBorder() {
// Logic for adding style
layer.backgroundColor = UIColor.white.cgColor
layer.borderColor = UIColor.gray.cgColor
layer.borderWidth = 0.0
layer.cornerRadius = 5
layer.masksToBounds = false
layer.shadowRadius = 2.0
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 1.0, height: 1.0)
layer.shadowOpacity = 1.0
layer.shadowRadius = 1.0
}
}
Hope this helps.
I agree with Silas, but just adding on here...
If you want to create a colorful bottom bar to a textfield, this also works:
import Foundation
import UIKit
class Utilities {
static func styleTextField(_ textfield:UITextField) {
// Create the bottom line
let bottomLine = CALayer()
bottomLine.frame = CGRect(x: 0, y: textfield.frame.height - 2, width: textfield.frame.width, height: 2)
bottomLine.backgroundColor = UIColor.init(red: 116/255, green: 203/255, blue: 128/255, alpha: 1).cgColor
// Remove border on text field
textfield.borderStyle = .none
// Add the line to the text field
textfield.layer.addSublayer(bottomLine)
}

Resources