I am beginner in Swift/Xcode. I made a custom class in my Xcode project to handle buttons appearance (color, shape etc) that works find.
However, I can't succeed to add gradients to these buttons.
I tried the below code, but it adds a new squared gradient onto my rounded buttons instead of applying the intended gradients to those buttons. Here is what I already gave many tries:
- add this code to my custom button class (inheriting from UIButton)
- add this code to my custom button class (inheriting from UIView)
- above 2 approaches and removing the backgroungColor settings
- Using a separate function called from viewController
func ConfigMenuButtons() {
// Set button shape color
layer.cornerRadius = 37
// Set button size
translatesAutoresizingMaskIntoConstraints = false
widthAnchor.constraint(equalToConstant: 74).isActive = true
heightAnchor.constraint(equalToConstant: 74).isActive = true
// Set button text
setTitleColor(.white, for: .normal)
titleLabel?.font = UIFont.boldSystemFont(ofSize: 15)
// Set button shadow
layer.shadowRadius = 2
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.8
// Set button gradient colors
let lightBlue = UIColor(red: 122/255, green: 127/255, blue: 249/255, alpha: 1)
let darkBlue = UIColor(red: 74/255, green: 88/255, blue: 205/255, alpha: 1)
// Set gradients
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [lightBlue.cgColor, darkBlue.cgColor]
gradientLayer.locations = [0.0,1.0]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
clipsToBounds = true
// insert gradients to button
layer.insertSublayer(gradientLayer, at: 0)
}
But I always end up with this "square shaped gradient layer" appearing onto the targeted buttons, instead of having these gradients applied directly to these buttons.
If someone had any piece of advice, it would be fantastic.
Thank you!
Here is the screen shot of the button with its partial expected effect
Here is a screenshot of fixed issue
I finally fixed the issue as below adding a CGRect size.
func ConfigMenuButtons() {
// Set button shape
layer.cornerRadius = 37
// Set button size
translatesAutoresizingMaskIntoConstraints = false
widthAnchor.constraint(equalToConstant: 74).isActive = true
heightAnchor.constraint(equalToConstant: 74).isActive = true
// Set button text
setTitleColor(.white, for: .normal)
titleLabel?.font = UIFont.boldSystemFont(ofSize: 15)
// Set button shadow
layer.shadowRadius = 2
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.8
// Set button gradient colors
let lightBlue = UIColor(red: 112/255, green: 117/255, blue: 239/255, alpha: 1)
let darkBlue = UIColor(red: 70/255, green: 84/255, blue: 201/255, alpha: 1)
// Set gradients
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [lightBlue.cgColor, darkBlue.cgColor]
gradientLayer.locations = [0.0,1.0]
gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
// THAT IS THE LINE THAT COULD FIX THE ISSUE
gradientLayer.frame = CGRect(x: 0, y: 0, width: 74, height: 74)
// Set rounded shape
gradientLayer.cornerRadius = 37
// insert gradients to button
layer.insertSublayer(gradientLayer, at: 0)
}
Related
So, I need a button in iOS / swift, that has outer shadow on bottom and right border, and inner shadow on top and left border. I was studying tutorials for several days, and came up with following solution that is ALMOST what I need.
internal func Setup() {
self.backgroundColor = .white
self.contentEdgeInsets = UIEdgeInsets(top: 10, left:30, bottom: 10, right: 30)
// Outer shadow
self.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor
self.layer.shadowOffset = CGSize(width: 10, height: 10)
self.layer.shadowOpacity = 1.0
self.layer.shadowRadius = 6
self.layer.masksToBounds = false
self.layer.cornerRadius = self.frame.height / 2
// call to add inner shadow
self.addInnerShadow()
}
internal func addInnerShadow() {
let cornerRadius = self.bounds.height / 2
let innerShadow = CALayer()
innerShadow.frame = bounds
// Shadow path (1pt ring around bounds)
let path = UIBezierPath(roundedRect: innerShadow.bounds.insetBy(dx: -1, dy: -1), cornerRadius: cornerRadius)
let cutout = UIBezierPath(roundedRect: innerShadow.bounds, cornerRadius: cornerRadius).reversing()
path.append(cutout)
innerShadow.shadowPath = path.cgPath
innerShadow.masksToBounds = true
// Shadow properties. Color is SAME as in outer shadow, but will come out differently
innerShadow.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor
innerShadow.shadowOffset = CGSize(width: 8, height: 8)
innerShadow.shadowOpacity = 1.0
innerShadow.shadowRadius = 6
innerShadow.cornerRadius = cornerRadius
// Add
layer.addSublayer(innerShadow)
}
Function Setup() is called during button's init(), and it all creates nice shadowed button with rounded corners.
But the question is - why the top inner shadow is so light? It should be of the same color as the bottom outer shadow. If I set any other color (like red), it is always very light, with only a tint of the required color. Why? Is it possible to control the color of inner shadow as directly as I do it with outer shadow?
I'm trying to implement a underlined UITextField with a gradient. Therefore I created a extension with a function underlined().
To get a gradient, I created a CAGradientLayer and made these customizations:
func underlined(){
let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1).cgColor
let sndColor = UIColor(red: 106/255, green: 178/255, blue: 255/255, alpha: 1).cgColor
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [color, sndColor]
gradient.locations = [0.0, 1.0]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
let width = CGFloat(2.0)
gradient.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
gradient.borderWidth = width
self.layer.insertSublayer(gradient, at: 0)
self.layer.masksToBounds = true
}
A underline is being displayed, but it solely solid black - I've tried to change the colors, but it remains black (Issue outdated - see edit).
Does anybody see the issue?
Edit:
Adding gradient.borderColor = UIColor.blue.cgColor let me change the color of the line - but how can I apply the gradient on the border color?
Remove the below line from your code:
gradient.borderWidth = width
Screenshot:
Your code was not working because, the borderWidth is covering whole of the gradient frame.Try setting the borderColor, then you'll see the difference.
gradient.borderColor = UIColor.red.cgColor
Let me know if you still face any issues.
I am working on an iOS project where I have white labels on bright images. The problem is for bright images the white labels are not showing. Here is an example:
Label not showing: https://imgur.com/hKtejHn
Label showing: https://imgur.com/Ef5qJAh
I think if I add a black gradient on all the image then the white labels will be visible. Can anyone help me as to how to implement the solution in Swift?
Thank!
If you want to add gradient on your imageView then you can just implement CAGradientLayer on your imageView.layer.
Try to change some values for your own custom look, but the code below is pretty much it.
let gradientLayer = CAGradientLayer()
gradientLayer.frame = imageView.frame
let colors = [
UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor,
UIColor(red: 0, green: 0, blue: 0, alpha: 0).cgColor
]
gradientLayer.startPoint = CGPoint(x: 0.1, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.9, y: 0.5)
gradientLayer.colors = colors
imageView.layer.addSublayer(gradientLayer)
You can change colors, add colors, change start/end-points. You can find a lot of different CAGradientLayer-guides on youtube or google.
try this:
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
}
}
usage:
label.lblShadow(color: UIColor.white, radius: 3, opacity: 0.75)
let gradient: CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
The resulting gradient runs off goes beyond the button's frame. Why does this happen?
Probably you are setting gradient layer in viewDidLoad() or viewWillAppear(). At that time controller did not calculated views sizes. At time you add this sublayer button size was not calculated yet, so sublayer size is wrong. So you should fix next things:
First of all you should add gradient at viewDidAppear(), at this point all view's sizes are calculated.
Second, you should use layer.insertSublayer(layer, atIndex:index) instead of addSublayer(layer). Because in your case sublayer will hide buttons native layer (title, background...)
You should recalculate your sublayer size in viewDidLayoutSubviews(). Because when your button will change it's size (while rotating for example), sublayer will not change it's frame, so you should change it by yourself.
Add clipsToBounds and cornerRadius to loginbutton. That should fix the problem.
loginButton.clipsToBounds = true
loginButton.layer.cornerRadius = 5;
Is your Login Button's frame correct?It seems correct when I reproduce
let loginButton = UIButton(frame: CGRect(x: 10, y: 50, width: 300, height: 30))
self.view.addSubview(loginButton)
let gradient:CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
And it appear like below
Is there a preferred method to add an auto-resizing gradient layer to a UIView or UILabel that will conform to the autolayout constraints set in IB (Xcode 6) and a wAny hAny canvass, using Swift?
For example, I can start with two labels that span universal widths using constraints using an wAny and hAny canvass, but when running the app on an iPad2 simulator, the labels size correctly but not the gradient layer.
I've looked into willAnimateRotationToInterfaceOrientation and adding a notification based on UIDeviceOrientationDidChangeNotification, but this is not helpful when the app is loaded in landscape (although once rotated the layers are redrawn and do match).
The only solution I have found is to manally adjust the width of each button to conform to the maximum view width of each (iPad2 in landscape) in the Utilities side panel in Xcode, but that creates "misplaced view" warnings and a messy canvass.
Any other ideas?
Here is what I have:
In viewDidLoad, I send each button through this:
Example:
//viewDidLoad --
self.configButton(self.button1!)
self.configButton(self.button2!)
self.configButton(self.button3!)
//
func configButton(theButton: UIButton){
let gradient: CAGradientLayer = CAGradientLayer()
gradient.frame.size = theButton.frame.size
theButton.layer.insertSublayer(gradient, atIndex: 0)
let topR = CGFloat(13.0)
let topG = CGFloat(55.0)
let topB = CGFloat(112.0)
let bottomR = CGFloat(90.0)
let bottomG = CGFloat(126.0)
let bottomB = CGFloat(167.0)
let colorTop = UIColor(red: CGFloat(topR/255.0), green: CGFloat(topG/255.0), blue: CGFloat(topB/255.0), alpha: 1.0).CGColor
let colorBottom = UIColor(red: CGFloat(bottomR/255.0), green: CGFloat(bottomR/255.0), blue: CGFloat(bottomR/255.0), alpha: 1.0).CGColor
let gradientColors: [AnyObject] = [colorTop, colorBottom]
gradient.colors = gradientColors
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
let theCornerRadius: CGFloat = 5.0
theButton.layer.cornerRadius = theCornerRadius
theButton.layer.masksToBounds = true
theButton.layer.borderWidth = 0.1
theButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
}
I would do this by subclassing UIButton, and putting the configureButton method, minus the layer sizing line, in that class. You can then set the frame of the gradient layer equal to the bounds of its super layer (the button's default layer) in layoutSublayersOfLayer because that layer automatically resizes to keep it the same size as the button. You don't need any code in the controller with this approach.
class RDButton: UIButton {
let gradient = CAGradientLayer()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configureButton()
}
func configureButton() {
self.layer.insertSublayer(gradient, atIndex: 0)
let topR = CGFloat(13.0)
let topG = CGFloat(55.0)
let topB = CGFloat(112.0)
let bottomR = CGFloat(90.0)
let bottomG = CGFloat(126.0)
let bottomB = CGFloat(167.0)
let colorTop = UIColor(red: CGFloat(topR/255.0), green: CGFloat(topG/255.0), blue: CGFloat(topB/255.0), alpha: 1.0).CGColor
let colorBottom = UIColor(red: CGFloat(bottomR/255.0), green: CGFloat(bottomR/255.0), blue: CGFloat(bottomR/255.0), alpha: 1.0).CGColor
let gradientColors: [AnyObject] = [colorTop, colorBottom]
gradient.colors = gradientColors
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
let theCornerRadius: CGFloat = 5.0
self.layer.cornerRadius = theCornerRadius
self.layer.masksToBounds = true
self.layer.borderWidth = 0.1
self.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
}
override func layoutSublayersOfLayer(layer: CALayer!) {
super.layoutSublayersOfLayer(layer)
gradient.frame = layer.bounds
}
}
Here is what I came up with to pass gradient colors to this new button class:
import Foundation
import UIKit
import QuartzCore
class RDButton: UIButton{
let gradient = CAGradientLayer()
var gradientColors: [Int] = []
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func configureButton(thecolors: [Int]) {
println("thecolors: \(thecolors)")
self.layer.insertSublayer(gradient, atIndex: 0)
let topR = CGFloat(thecolors[0])
let topG = CGFloat(thecolors[1])
let topB = CGFloat(thecolors[2])
let bottomR = CGFloat(thecolors[3])
let bottomG = CGFloat(thecolors[4])
let bottomB = CGFloat(thecolors[5])
let colorTop = UIColor(red: CGFloat(topR/255.0), green: CGFloat(topG/255.0), blue: CGFloat(topB/255.0), alpha: 1.0).CGColor
let colorBottom = UIColor(red: CGFloat(bottomR/255.0), green: CGFloat(bottomR/255.0), blue: CGFloat(bottomR/255.0), alpha: 1.0).CGColor
let gradientColors: [AnyObject] = [colorTop, colorBottom]
gradient.colors = gradientColors
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
let theCornerRadius: CGFloat = 5.0
self.layer.cornerRadius = theCornerRadius
self.layer.masksToBounds = true
self.layer.borderWidth = 0.1
self.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
}
override func layoutSublayersOfLayer(layer: CALayer!) {
super.layoutSublayersOfLayer(layer)
gradient.frame = layer.bounds
}
}
In the View Controller, I did this:
#IBOutlet weak var mySweetButton: RDButton?
// etc for each button
let buttonsgradient = [13,55,112,90,126,167]
override func viewDidLoad(){
super.viewDidLoad()
//...
mySweetButton?.configureButton(buttonsgradient)
/// etc for each button
// ...
}
Probably not the optimal way of doing this ...