custom UIButton class - gradient going out off button borders - swift

I'm doing custom UIButton class and I want it to have gradient background. My gradient color is looking perfect but it goes out of button borders
My code:
class CustomButton: UIButton{
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){
layer.cornerRadius = 25.0
layer.borderWidth = 1
gradientLayer.frame = layer.bounds
addShadow()
}
private lazy var gradientLayer: CAGradientLayer = {
let color1 = UIColor(red: 254.0/255.0, green: 79.0/255.0, blue: 50.0/255.0, alpha: 1.0).cgColor
let color2 = UIColor(red: 255.0/255.0, green: 26.0/255.0, blue: 107.0/255.0, alpha: 1.0).cgColor
let gradient = CAGradientLayer()
gradient.frame = layer.bounds
gradient.colors = [color1, color2]
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPoint(x: 1, y: 0.5)
gradient.locations = [0,1]
gradient.cornerRadius = 25
layer.insertSublayer(gradient, at: 0)
return gradient
}()
func addShadow(){
layer.shadowOpacity = 1
layer.shadowRadius = 1.0
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 1
layer.shadowOffset = CGSize(width: 0, height: 3)
}
}
And here's how it looks:

Remove gradientLayer.frame from setup and add in layoutSubviews.
Also set self.bounds instead of layer.bounds.
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = self.bounds // < Here
}
Note : Remove gradient.frame = layer.bounds from var gradientLayer. Not needed.

Just override layerClass to avoid resizing issue (Auto resize with constrains)
class CustomButton: UIButton{
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override class var layerClass: AnyClass {
CAGradientLayer.self
}
var gradientLayer: CAGradientLayer {
layer as! CAGradientLayer
}
func setup(){
gradientLayer.cornerRadius = 25.0
gradientLayer.borderWidth = 1
let color1 = UIColor(red: 254.0/255.0, green: 79.0/255.0, blue: 50.0/255.0, alpha: 1.0).cgColor
let color2 = UIColor(red: 255.0/255.0, green: 26.0/255.0, blue: 107.0/255.0, alpha: 1.0).cgColor
gradientLayer.colors = [color1, color2]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
gradientLayer.locations = [0,1]
gradientLayer.shadowOpacity = 1
gradientLayer.shadowRadius = 1.0
gradientLayer.shadowColor = UIColor.black.cgColor
gradientLayer.shadowOpacity = 1
gradientLayer.shadowOffset = CGSize(width: 0, height: 3)
}
}

Related

Shadow at top side of UIView only

How can I remove shadow from bottom side of my view?
This is code for my shadowView:
class ShadowViewWhite: UIView {
init() {
super.init(frame: .zero)
backgroundColor = .white
layer.shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.65, alpha: 0.9).cgColor
layer.shadowOpacity = 1
layer.shadowRadius = 40
layer.shadowOffset = CGSize(width: 0, height: -12)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
That's how it looks so far. I need shadow to be only at the top side of my view.
A combination of reducing the shadowRadius and shadowOffset should help you. I have attached my code below which should help you.
class ShadowViewWhite: UIView {
override init(frame:CGRect) {
super.init(frame: frame)
backgroundColor = .white
layer.shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.65, alpha: 0.9).cgColor
layer.shadowOpacity = 1
layer.shadowRadius = 20
layer.shadowOffset = CGSize(width: 0, height: -40)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
This code will produce the following image:
Change shadowRadius property. Ex:
layer.shadowRadius = 24
Using lower values of shadow radius can help. You can try using shadowRadius = 20.

Adding custom UIView to the view controller causes it to disappear in the storyboard

I created a custom UIView and then added it to a number of viewControllers. The view controller become glitchy and do not render in the storyboard. I just see the view controllers' outlines and no content. If I remove the custom view then all goes back to normal.
Can someone please take a look at my custom view code and see if I am doing anything wrong? Thank you so much.
import UIKit
#IBDesignable
class ProgressView: UIView {
#IBOutlet var contentView: UIView!
#IBInspectable var percentageProgress: CGFloat = 0.0 {
didSet {
self.setGradient()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("ProgressView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
setGradient()
}
private func setGradient() {
if let sublayers = self.contentView.layer.sublayers {
for sublayer in sublayers {
sublayer.removeFromSuperlayer()
}
}
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor(#colorLiteral(red: 0.3935321569, green: 0.3986310661, blue: 0.6819975972, alpha: 1)).cgColor, UIColor(#colorLiteral(red: 0.1705667078, green: 0.649338007, blue: 0.5616513491, alpha: 1)).cgColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
gradientLayer.cornerRadius = contentView.frame.height / 2
contentView.layer.cornerRadius = contentView.frame.height / 2
self.contentView.layer.insertSublayer(gradientLayer, at: 0)
gradientLayer.frame = CGRect(x: contentView.frame.minX, y: contentView.frame.minY,
width: self.contentView.frame.maxX * (percentageProgress ?? 1.0), height:
contentView.frame.maxY)
self.contentView.setNeedsDisplay()
}
}

Why are UIViews background color is not updating?

Switch statement works but won't reset view background colours etc.
I have a UIImage (icon) and a UIButton embedded within a UIView (of custom type DropShadowCircleView) as per image below.
When the walking button is tapped a var navigationOption is set to either walking or driving and setupNavigationSelectionView() is executed.
Problem is: case "walking" of the switch works perfectly, but case "driving" doesn't reset the UIView and icon tint color witcback to their original setting eg; background color etc.. any ideas why?
func setupNavigationSelectionView(){
switch navigationOption {
case "walking":
walkingBg.setGradientBackground(colourOne: softGreen, ColourTwo: softBlue)
walkingBg.layer.cornerRadius = walkingBg.frame.width / 2
walkingBg.clipsToBounds = true
walkingIcon.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
case "driving":
walkingBg.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
walkingBg.layer.cornerRadius = walkingBg.frame.width / 2
walkingBg.clipsToBounds = true
walkingIcon.tintColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
default:
break
}
}
EDIT: this is my DropShadowCircleView class
class DropShadowCircleView: UIView {
override func awakeFromNib() {
setupView()
super.awakeFromNib()
}
func setupView(){
self.layer.shadowOpacity = 0.50
self.layer.shadowRadius = 20
self.layer.shadowColor = UIColor.black.cgColor
self.layer.cornerRadius = self.frame.width / 2
}
}
EDIT: This is my setGradientBackground function which is within an extension file to UIView
func setGradientBackground(colourOne: UIColor, ColourTwo: UIColor) {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [colourOne.cgColor, ColourTwo.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.startPoint = CGPoint(x: 1.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0)
layer.insertSublayer(gradientLayer, at: 0)
}
You need to remove your gradient layer when you reset your icon.
Add this to your extension UIView:
func removeGradientBackground() {
guard
let idx = layer.sublayers?.index(where: { $0 is CAGradientLayer })
else { return }
layer.sublayers?.remove(at: idx)
}
and call it when you are resetting your icon.

Creating custom Color for UIColor

I have a question about creating a custom UIColor in swift.
I used the following extension code:
import UIKit
extension UIColor {
convenience init(red: Int, green: Int, blue: Int) {
let newRed = CGFloat(red)/255
let newGreen = CGFloat(green)/255
let newBlue = CGFloat(blue)/255
self.init(red: newRed, green: newGreen, blue: newBlue, alpha: 1.0)
}
}
let brandBlue = UIColor(red: 0, green: 127, blue: 255)
I want to load it in, shadowColor, under the following code:
class squareButton : UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 10.0
self.layer.shadowColor = (Loading code here)
self.layer.shadowRadius = 1.5
self.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
self.layer.shadowOpacity = 0.6
self.layer.masksToBounds = false
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = 10.0
self.layer.shadowColor = (Loading code here)
self.layer.shadowRadius = 1.5
self.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
self.layer.shadowOpacity = 0.6
self.layer.masksToBounds = false
}
}
But every combination I used won't work. Every time it says that I should use as! CGColor but when I do that, the app crashes.
Please help me
Thank you
In your code you are currently providing to self.layer.shadowColor = (Loading code here) a UIColor this should cause a compiler error.
The extension you created is fine. Just use the .cgColor property of UIColor after you instantiated your new UIColor instance. This would then be the example code:
class SquareButton: UIButton {
let brandBlue: UIColor = UIColor(red: 0, green: 127, blue: 255)
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = 10.0
self.layer.shadowColor = brandBlue.cgColor
self.layer.shadowRadius = 1.5
self.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
self.layer.shadowOpacity = 0.6
self.layer.masksToBounds = false
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = 10.0
self.layer.shadowColor = brandBlue.cgColor
self.layer.shadowRadius = 1.5
self.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
self.layer.shadowOpacity = 0.6
self.layer.masksToBounds = false
}
}
CGColor and UIColorare not the same and CALayer.shadowColor needs to be a CGColor. You can get a CGColor from a UIColor by calling the cgColor property on a UIColor instance.
Like this: self.layer.shadowColor = UIColor(red: 0, green: 127, blue: 255).cgColor

my drawRect function will not update

I want the values for the colors of the circle to update when the variables(redd1,greenn1,bluee1) are changed by the steppers in my ViewController. The original circle is drawn by drawRect.
var redd1 = 0.0;
var greenn1 = 0.0;
var bluee1 = 0.0;
override init(frame: CGRect)
{
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
override func drawRect(rect: CGRect)
{
let circle2 = UIView(frame: CGRect(x: -25.0, y: 10.0, width: 100.0, height:100.0))
circle2.layer.cornerRadius = 50.0
let startingColor2 = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat (greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
circle2.backgroundColor = startingColor2;
addSubview(circle2);
}
I tried creating a new function that draws a new circle on top of the old one. It is called by the stepper. The new function, updateColor(), receives the new value from the stepper. This partially works because it prints out the correct new values, but never draws the new circle.
func updateColor()
{
let circle = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
circle.layer.cornerRadius = 50.0;
let startingColor = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat(greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
circle.backgroundColor = startingColor;
addSubview(circle);
}
I'm not so good in Swift, but that's a simple way how i wood do it:
var circle2: UIView
override func drawRect(rect: CGRect) {
circle2 = UIView(frame: CGRect(x: -25.0, y: 10.0, width: 100.0, height:100.0))
circle2.layer.cornerRadius = 50.0
let startingColor2 = UIColor(red: (CGFloat(redd1))/255, green: (CGFloat (greenn1))/255, blue: (CGFloat(bluee1))/255, alpha: 1.0)
circle2.backgroundColor = startingColor2;
addSubview(circle2);
}
#IBAction valueChanged(sender: UIStepper) {
changeColor(color(the Color you want), Int(the Int value you want the color to change at))
}
func changeColor(color: UIColor, number: Int) {
if stepper.value == number {
circle2.backgroundColor = color
}
}
You have to create a IBAction for the stepper of the type value changed and call the method for every color and to it belonging Int in the IBAction for the stepper. So every time + or - on the stepper is pressed the IBAction calls the changeColor method and the changeColor method tests which color should be set to circle2.