-2

I have a custom button with some shadow effect, following class i am using to create a custom button which i got from stackoverflow. It users CAShapeLayer to give effect of shadow on button.

import UIKit

class CustomButton: UIButton {

var shadowLayer: CAShapeLayer!
override func layoutSubviews() {
    super.layoutSubviews()

    if shadowLayer == nil {
        shadowLayer = CAShapeLayer()
        shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath

        shadowLayer.fillColor = UIColor.white.cgColor
        shadowLayer.shadowColor = UIColor.darkGray.cgColor
        shadowLayer.shadowPath = shadowLayer.path
        shadowLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
        shadowLayer.shadowOpacity = 0.8
        shadowLayer.shadowRadius = 2

        layer.insertSublayer(shadowLayer, at: 0)
        //layer.insertSublayer(shadowLayer, below: nil) // also works
    }
}
}

Following is an image of screen where i have created 4 buttons using this CustomButton Class which works fine.

enter image description here

when any button from above four is clicked i changed it's following property so it can look like an active button.

// to make button active
    func setSelectedButton(sender:CustomButton){
        //sender.backgroundColor = UIColor.red
        sender.shadowLayer.fillColor = UIColor(named: "headerColor")?.cgColor
        sender.setTitleColor(UIColor.white, for: .normal)
    }
// to make button inactive 
    func setUnselected(sender:CustomButton){
        //sender.backgroundColor = UIColor.init(red: 80/255, green:101/255, blue: 161/255, alpha: 1)
        sender.shadowLayer.fillColor = UIColor.white.cgColor
        sender.setTitleColor(.black, for: .normal)
    }

Everything works fine. Now what i want is whenever view appears i want the first button to be selected by default which is routineButton to do that i have written following code in viewWillAppearMethod

override func viewWillAppear(_ animated: Bool) {
        self.navigationItem.title = navigationTitle
        self.view.makeToastActivity(.center)
        self.routineButton.sendActions(for: .touchUpInside) //default selection of routineButton
        loadEvaluationList(userId: 8, evaluationType: "RT") {
            self.view.hideToastActivity()
        }
    }

when self.routineButton.sendActions(for: .touchUpInside) executes it call setSelectedButton(sender:CustomButton) method which gives error at following line

 sender.shadowLayer.fillColor = UIColor(named: "headerColor")?.cgColor

which says shadowLayer is nil. This problem occur only when i try to set default selected button on viewWillAppear Method otherwise it works perfact. I think the problem occur because shadowLayer property of CustomButton is not initialised at time of viewWillAppear. so anyone knows what should i do? it will be helpful. Thank you in advance.

Naresh
  • 16,698
  • 6
  • 112
  • 113
Deepak
  • 1,030
  • 2
  • 10
  • 21
  • First you assign shadow colour and all effects, then call uibutton selection. I think here you need to check line by line execution. – Naresh May 14 '18 at 10:36
  • Because viewWillAppear called first. But you assigned shadow effects in layoutSubViews. See this link of execution flow https://stackoverflow.com/questions/5562938/looking-to-understand-the-ios-uiviewcontroller-lifecycle – Naresh May 14 '18 at 10:41

1 Answers1

0

Move the initialization code for shadowLayer in the init methods, something like this:

import UIKit

class CustomButton: UIButton {

    var shadowLayer: CAShapeLayer!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.initShadowLayer()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.initShadowLayer()
    }

    func initShadowLayer() {
        if shadowLayer == nil {
            shadowLayer = CAShapeLayer()
            shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath

            shadowLayer.fillColor = UIColor.white.cgColor
            shadowLayer.shadowColor = UIColor.darkGray.cgColor
            shadowLayer.shadowPath = shadowLayer.path
            shadowLayer.shadowOffset = CGSize(width: 2.0, height: 2.0)
            shadowLayer.shadowOpacity = 0.8
            shadowLayer.shadowRadius = 2

            layer.insertSublayer(shadowLayer, at: 0)
        }

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // Reset the path because bounds could have been changed
        shadowLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 12).cgPath
        shadowLayer.shadowPath = shadowLayer.path
    }
}

viewWillAppear is called before layoutSubviews of the button and shadowLayer is not initialized yet. Also try not to call sendActions(for: .touchUpInside), instead call the setSelectedButton function if you just want to configure the button appearance.

LorenzOliveto
  • 7,796
  • 1
  • 20
  • 47
  • Thank you sir for your answer. it is working perfactly fine. I am new to the swift and ios programming. – Deepak May 14 '18 at 12:01