Custom UIButton subclass - ios

I'm trying my own custom UIButton. I subclassed the UIButton class and tried to add some functions this is my code:
import UIKit
class CustomButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
setGradientBackground()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
private func setup() {
layer.cornerRadius = 8
}
private func setGradientBackground() {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [UIColor.yellow.cgColor, UIColor.blue.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)
}
}
I linked it to my IBOutlet :
#IBOutlet weak var myButton: CustomButton!
And nothing apply !

If you're adding the button in a storyboard, the initialiser that's called is init?(coder aDecoder: NSCoder), not init(frame: CGRect), so you need to add…
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
setGradientBackground()
}

Use func awakeFromNib() :
class CustomButton: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
setup()
setGradientBackground()
}
private func setup() {
layer.cornerRadius = 8
}
private func setGradientBackground() {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [UIColor.yellow.cgColor, UIColor.blue.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)
}
}

Related

Apply Shadow along with the gradient colour with custom class

I write the one custom class for gradient button, I want to apply shadow also for that.
I getting Gradient colour but I don't get the shadow.
Here I think I have issue with self.clipsToBounds = true
find my code with following.
class GradientButton: UIButton {
let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
layer.cornerRadius = self.frame.size.height / 2.0
gradientLayer.locations = [0.0, 1.0]
// top-right to bottom-left
gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
// we only add the layer once, here when called from init
layer.addSublayer(gradientLayer)
self.clipsToBounds = true
}
func setGradientBackground(colorOne: UIColor, colorTwo: UIColor) {
gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor]
applyShadow(shadoeclr: colorOne)
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
func applyShadow(shadoeclr:UIColor){
layer.shadowColor = shadoeclr.cgColor
layer.shadowRadius = 5
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: 0, height: 0)
}
}
try to use masksToBounds instead of clipsToBounds

Horizontal Gradient colour Showing Half only in Swift?

I I am trying to give the Gradient color for Button Baground, I am trying with following code
func setGradientBackground(colorOne: UIColor, colorTwo: UIColor) {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
layer.insertSublayer(gradientLayer, at: 0)
}
#IBOutlet weak var loginbtn: UIButton!
loginbtn.setGradientBackground(colorOne: UIColor.red, colorTwo: UIColor.blue)
I am trying to access this method it will show the output like this How to we fix this,
If I add following code
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
loginbtn.setGradientBackground(colorOne: UIColor.red, colorTwo: UIColor.green)
}
it will show the following error.
Make sure you are setting the gradient frame after auto-layout has set the size of the button.
If you subclass the button, you can do so in layoutSubviews().
Otherwise, A good place to do this in your view controller is in viewDidLayoutSubviews():
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
loginbtn.setGradientBackground(colorOne: UIColor.red, colorTwo: UIColor.blue)
}
Edit
Based on the second image you posted - getting over-lapping gradients when called from viewDidLayoutSubviews()... As auto-layout "does its thing," that can be called more than once, and you are adding a new layer each time that is called.
I don't know what else you might be doing in your custom class, but give it a try like this:
class GradButton: UIButton {
let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gradientLayer.locations = [0.0, 1.0]
// top-right to bottom-left
gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
// we only add the layer once, here when called from init
layer.addSublayer(gradientLayer)
}
func setGradientBackground(colorOne: UIColor, colorTwo: UIColor) {
gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor]
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
}
class MyTest: UIViewController {
#IBOutlet var loginbtn: GradButton!
override func viewDidLoad() {
super.viewDidLoad()
loginbtn.setGradientBackground(colorOne: .red, colorTwo: .yellow)
}
}
Result:

How can i see the drawing in storyboard?

I drawed triangle but I can't see on storyboard. How can I see on storyboard this drawing ?
View Controller Code:
class ViewController: UIViewController {
#IBOutlet weak var fooView: UIView!
var polygonShape = CAShapeLayer()
override func viewDidLoad() {
super.viewDidLoad()
let polygonPath = UIBezierPath()
polygonPath.move(to: CGPoint(x: 50, y: -1))
polygonPath.addLine(to: CGPoint(x: 93.3, y: 74))
polygonPath.addLine(to: CGPoint(x: 6.7, y: 74))
polygonPath.close()
polygonShape.frame = fooView.bounds
polygonShape.path = polygonPath.cgPath
fooView.layer.mask = polygonShape
}
}
Change your class to this:
#IBDesignable
class FirstImageView: UIView {
var polygonShape: CAShapeLayer!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
if polygonShape == nil {
polygonShape = CAShapeLayer()
layer.mask = polygonShape
}
}
override func layoutSubviews() {
super.layoutSubviews()
let polygonPath = UIBezierPath()
polygonPath.move(to: CGPoint(x: 0.0, y: bounds.size.height))
polygonPath.addLine(to: CGPoint(x: bounds.width * 0.5, y: 0.0))
polygonPath.addLine(to: CGPoint(x: bounds.size.width, y: bounds.size.height))
polygonPath.close()
polygonShape.path = polygonPath.cgPath
}
}
You can remove everything from your ViewController class. You don't even need the class at all at this point.
Select the view on your Storyboard, and at the top of the Identity Inspector pane change the Custom Class from the default UIView to FirstImageView.
Under the top Editor menu, either select Refresh All Views or make sure Automatically Refresh Views is checked.
Result:

Gradient addition in UITableviewCell causing animation issue on reloading

I want to make chat screen, and the chat bubbles have some gradient. While applying gradient to UIView its creating problem in reloading.
GradientView has been setup as the class of UIView which is placed on UITableViewCell
class GradientView: UIView {
let gradientLayer = CAGradientLayer()
func setup() {
layer.addSublayer(gradientLayer)
}
func configure() {
gradientLayer.frame = self.bounds
gradientLayer.colors = [RGBA(r: 167, g: 1, b: 88, a: 1.0).cgColor, RGBA(r: 243, g: 135, b: 32, a: 1.0).cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
override func layoutSubviews() {
super.layoutSubviews()
setup()
configure()
}
}
The problem is while reloading the table view the gradient shows up with animation.
Many properties you set on CALayer and its subclasses will be animated by default. For a gradient layer, this includes the bounds, the gradient colours, and the start and end points, all of which you are setting here.
This is called implicit animation.
You can turn it off by creating a transaction, disabling actions, performing your changes, then committing the transaction:
CATransaction.begin()
CATransaction.setDisableActions(true)
// Do your stuff
CATransaction.commit()
However, that can get a bit messy. An alternative is to create a non-animating layer subclass and use that instead of CAGradientLayer:
class NonAnimatingGradientLayer: CAGradientLayer {
override func action(forKey event: String) -> CAAction? {
return NSNull()
}
}
Use this instead of CAGradientLayer and nothing will be animated.
layoutSubviews is called many times
class GradientView: UIView {
var gradientLayer:CAGradientLayer!
func setup() {
gradientLayer = CAGradientLayer()
layer.addSublayer(gradientLayer)
gradientLayer.frame = self.bounds
gradientLayer.colors = [RGBA(r: 167, g: 1, b: 88, a: 1.0).cgColor, RGBA(r: 243, g: 135, b: 32, a: 1.0).cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
}
override func layoutSubviews() {
super.layoutSubviews()
if gradientLayer == nil {
setup()
}
}
}

Gradient view not working when added programmatically

So my Class looks like this :
import UIKit
class GradientBarView: UIView {
override init(frame: CGRect) { // for using CustomView in code
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPointMake(1, 0.5)
gradient.colors = [AppColor.SecondaryGreen.CGColor, AppColor.HeritageGreen.CGColor, AppColor.HeritageGreen.CGColor]
layer.insertSublayer(gradient, atIndex: 0)
translatesAutoresizingMaskIntoConstraints = false
}
}
and this is how I use it(in my viewDidLoad)
let gradientBar = GradientBarView(frame: CGRectZero)
self.view.addSubview(gradientBar)
Then add the necessary constraints(which is fine)...so the problem is that the view is just black but if i add it via the Storyboard it works just fine
please help,what am i missing?
Thanks
Your frame is CGRectZero by the time you call gradient.frame = bounds in commonInit. Thus your bounds will also be CGRectZero. Thus the frame you are inserting has no height or width.
You can add the gradient in the draw method of your custom view. override func draw(_ rect: CGRect) Just ensure that you do not add it more than once.
Alternatively add it to layoutSubviews, depending on your use. Something like:
class GradientBarView: UIView {
var gradientLayer: CAGradientLayer?
override init(frame: CGRect) { // for using CustomView in code
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
translatesAutoresizingMaskIntoConstraints = false
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer?.removeFromSuperlayer()
gradientLayer = self.addHorizontalGradient(color: UIColor.blue)
}
}
extension UIView {
func addHorizontalGradient(color: UIColor) -> CAGradientLayer {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.startPoint = CGPoint(x:0,y:0.5)
gradient.endPoint = CGPoint(x:1,y:0.5)
gradient.colors = [color.cgColor, UIColor.white.withAlphaComponent(0).cgColor]
self.layer.insertSublayer(gradient, at: 0)
return gradient
}
}
I had the same issue. If I added a view to viewController at interface builder then gradient was displayed. But if I did it programmatically in this case gradient was absent.
This is the code that doesn't work properly.
import UIKit
class HeaderLandscape: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupFromNib()
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupFromNib()
setupView()
}
private func setupView() {
setGradientBackground()
}
private func setGradientBackground() {
let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: self.bounds.height)
gradient.colors = [UIColor(red: 0, green: 0, blue: 0, alpha: 0.32).cgColor,
UIColor(red: 0, green: 0, blue: 0, alpha: 0).cgColor]
gradient.startPoint = CGPoint(x: 0.5, y: 0)
gradient.endPoint = CGPoint(x: 0.5, y: 1)
self.layer.insertSublayer(gradient, at: 0)
}
}
and moving setGradientBackground() function call from setupView() into hext method fixed the problem.
override func layoutSubviews() {
super.layoutSubviews()
setGradientBackground()
}

Resources