swiftuikit

How to create buttons in stack view?


I trying to use UIView instead UIButton as @duckSern1108 recommended.

I created a custom class for UIView and created touch animation inside the class. Also in my main view controller I create UIView class instance and add a UITapGestureRecouncer for UIView to repeat the button action behaviour. But after adding UITapGestureRecouncer in main view controller the touchesEnded function is not called inside the class. How to fix it?

class SLView: UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        animateScale(to: 0.9, duration: 0.4)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        animateScale(to: 1, duration: 0.38)
    }
    
    private func animateScale(to scale: CGFloat, duration: TimeInterval) {
        UIView.animate(withDuration: duration,
                       delay: 0,
                       usingSpringWithDamping: 0.5,
                       initialSpringVelocity: 1.0,
                       options: [],
                       animations: {
            self.transform = CGAffineTransform(scaleX: scale, y: scale)
        }, completion: nil)
    }
    
}

main vc


let settingsMenu = UIStackView()
var itemView = SLView()

func contextMenu() {
    
    for index in 1...2 {
        
         itemView = SLView()
         itemView.tag = index
         itemView.backgroundColor = .white.withAlphaComponent(0.75)
         itemView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
         itemView.layer.borderColor = UIColor.white.cgColor
         itemView.layer.borderWidth = 4
         itemView.layer.cornerRadius = 24
         itemView.layoutMargins = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
         itemView.translatesAutoresizingMaskIntoConstraints = false
         settingsMenu.addArrangedSubview(itemView)
                
         itemView.widthAnchor.constraint(equalToConstant: 232).isActive = true
         itemView.heightAnchor.constraint(equalToConstant: 32).isActive = true
        }
        settingsMenuItemImage.widthAnchor.constraint(equalToConstant: 32).isActive = true
        settingsMenuItemImage.heightAnchor.constraint(equalToConstant: 32).isActive = true
        itemView.setCustomSpacing(16.0, after: itemView.subviews[0])
    }
        
    let contactGesture = UITapGestureRecognizer(target: self, action:  #selector(self.contactWithDeveloper))
    self.settingsMenu.subviews[0].addGestureRecognizer(contactGesture)
    let restoreGesture = UITapGestureRecognizer(target: self, action:  #selector(self.restorePurchase))
    self.settingsMenu.subviews[1].addGestureRecognizer(restoreGesture)
        
}

@objc func another(_ sender: UITapGestureRecognizer) {
    print("purchase")
}

@objc func restorePurchase(_ sender: UITapGestureRecognizer) {
    print("purchase")
}

Solution

  • When you add a UITapGestureRecognizer, when user lift finger, touchesEnded is not forward from UITapGestureRecognizer to your SLView (as you see when testing), but touchesCancelled is forwarded. So to animate, you should override touchesCancelled.

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        animateScale(to: 1, duration: 0.38)
    }
    

    But here you don't need to combine UITapGestureRecognizer with touchesBegan and touchesEnded just provide a closure for SLView and call it when touchesEnded:

    var onTap: (() -> Void)?
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        animateScale(to: 1, duration: 0.38)
        onTap?()
    }