title: What is best practice for creating a reusable custom button views?
body: <p>I have three buttons below that have the same UI, the only differences are the text for the labels and tap gesture actions. It looks like this:</p>
  <p>What is the best practice for creating a reusable custom button view based on this situation?</p>
  <p>So far I tried using: (1) custom button class but had difficulty implementing a stack view where I can configure the two labels in the button, (2) UIButton extension but an issue where tapping the button caused the app to crash</p>
  <pre><code>class SetActivityVC: UIViewController {
   
      override func viewDidLoad() {
          super.viewDidLoad()
          
          setupViews()
      }
      
      lazy var firstButton: UIButton = {
          let button = UIButton()
          
          let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapFirst))
          button.addGestureRecognizer(tapGesture)
          
          button.setBackgroundImage(Image.setButtonBg, for: .normal)
          button.addShadowEffect()
          
          let label = UILabel()
          label.text = &quot;No Exercise&quot;
          label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
          label.textColor = .black
          
          let subLabel = UILabel()
          subLabel.text = &quot;no exercise or very infrequent&quot;
          subLabel.font = UIFont.systemFont(ofSize: 12, weight: .regular)
          subLabel.textColor = .gray
          
          let stack = UIStackView(arrangedSubviews: [label, subLabel])
          stack.axis = .vertical
          stack.alignment = .center
          stack.isUserInteractionEnabled = true
          stack.addGestureRecognizer(tapGesture)
          
          button.addSubview(stack)
          stack.centerInSuperview()
          
          return button
      }()
      
      lazy var secondButton: UIButton = {
          let button = UIButton()
          let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapSecond))
          button.addGestureRecognizer(tapGesture)
          
          button.setBackgroundImage(Image.setButtonBg, for: .normal)
          button.addTarget(self, action: #selector(didTapSecond), for: .touchUpInside)
          button.addShadowEffect()
          
          let label = UILabel()
          label.text = &quot;Light Exercise&quot;
          label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
          label.textColor = .black
          
          let subLabel = UILabel()
          subLabel.text = &quot;some light cardio/weights a few times per week&quot;
          subLabel.font = UIFont.systemFont(ofSize: 12, weight: .regular)
          subLabel.textColor = .gray
          
          let stack = UIStackView(arrangedSubviews: [label, subLabel])
          stack.axis = .vertical
          stack.alignment = .center
          
          button.addSubview(stack)
          stack.centerInSuperview()
          
          return button
      }()
      
      lazy var thirdButton: UIButton = {
          let button = UIButton()
          let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapThird))
          button.addGestureRecognizer(tapGesture)
          
          button.setBackgroundImage(Image.setButtonBg, for: .normal)
          button.addTarget(self, action: #selector(didTapSecond), for: .touchUpInside)
          button.addShadowEffect()
          
          let label = UILabel()
          label.text = &quot;Moderate Exercise&quot;
          label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
          label.textColor = .black
          
          let subLabel = UILabel()
          subLabel.text = &quot;lifting/cardio regularly but not super intense&quot;
          subLabel.font = UIFont.systemFont(ofSize: 12, weight: .regular)
          subLabel.textColor = .gray
          
          let stack = UIStackView(arrangedSubviews: [label, subLabel])
          stack.axis = .vertical
          stack.alignment = .center
          
          button.addSubview(stack)
          stack.centerInSuperview()
          
          return button
      }()
      
      @objc func didTapFirst() {
          print(&quot;Tapped 1&quot;)
      }
      
      @objc func didTapSecond() {
          print(&quot;Tapped 2&quot;)
      }
      
      @objc func didTapThird() {
          print(&quot;Tapped 3&quot;)
      }
  }
  
  extension SetActivityVC {
      fileprivate func setupViews() {
          addViews()
          constrainViews()
      }
      
      fileprivate func addViews() {
          view.addSubview(firstButton)
          view.addSubview(secondButton)
          view.addSubview(thirdButton)
      }
      
      // Using TinyConstraints 
      fileprivate func constrainViews() {
          firstButton.centerXToSuperview()
          
          secondButton.centerXToSuperview()
          secondButton.topToBottom(of: firstButton, offset: screenHeight * 0.03)
          
          thirdButton.centerXToSuperview()
          thirdButton.topToBottom(of: secondButton, offset: screenHeight * 0.03)
      }
  }
  </code></pre>