Search code examples
iosswiftuibuttonuser-interaction

Swift - How to make UIButton tap Interaction only on the image attached and ignore transparent parts


I am creating buttons with different image shapes... Everything is working but if for example, a diamond shape is created, you can click on the transparent part of the button. I managed to set up the shadow to respect the shape of the button so it looks highlighted but how do I apply this so the transparent part of the buttons is ignored when interacted with?

I found some answers but I do not use paths to create the UIButton shape but an image on the UIButton.

enter image description here

Below is how to create one of my buttons and set the glow

extension UIbutton {
  func createSquareButton(buttonPositionX: CGFloat, buttonPositionY: CGFloat, buttonWidth: CGFloat, buttonHeight: CGFloat, buttonTitle: String) {
    let button = self
    button.isUserInteractionEnabled = true
    button.frame = CGRect(x: buttonPositionX, y: buttonPositionY, width: buttonWidth, height: buttonHeight)
    button.setTitle(buttonTitle, for: .normal)
    button.setTitleColor(UIColor.white, for: .normal)
    button.setTitleColor(UIColor.black, for: .highlighted)
    button.alpha = 1.0
    button.titleLabel?.font = .systemFont(ofSize: 20)
    button.backgroundColor = nil
    button.tintColor = Style.PurpleColor

    let image = UIImage(named: "Plain Square")
    let imageView = UIImageView(image: image)
    imageView.setImageColor(color: Style.PurpleColor)
    button.setBackgroundImage(imageView.image, for: .normal)
    button.setBackgroundImage(imageView.image, for: .selected)
    button.setBackgroundImage(imageView.image, for: .highlighted)
   }
}

 func buttonGlow(sender: CustomBtn){

    for button in buttonArray {
        if button.UUIDtag == currentSelectedMarkerUUID {

            sender.layer.shadowColor = button.tintColor.cgColor
            sender.layer.shadowRadius = 15
            sender.layer.shadowOpacity = 1.0
            sender.layer.masksToBounds = false

        } else {
            let otherBtn = tmpButtonPicked(uuid: button.UUIDtag!)
            otherBtn.layer.shadowColor = UIColor.clear.cgColor
            otherBtn.layer.shadowRadius = 0
        }
    }
}

extension UIImageView {
func setImageColor(color: UIColor) {
    let templateImage = self.image?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
    self.image = templateImage
    self.tintColor = color
  }
}

Solution

  • Thanks to Malik's help I solved the problem.

    Firstly you need to implement the function below which recognizes what is the alpha value where you tap within the button.

     func alphaFromPoint(sender: CustomBtn, point: CGPoint) -> CGFloat {
        var pixel: [UInt8] = [0, 0, 0, 0]
        let colourSpace = CGColorSpaceCreateDeviceRGB()
        let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colourSpace, bitmapInfo: alphaInfo.rawValue)
    
        context?.translateBy(x: -point.x, y: -point.y)
    
        sender.layer.render(in: context!)
    
        let floatAlpha = CGFloat(pixel[3])
        return floatAlpha
    }
    

    then I tweaked @IBaction to also receive the type of event. I then Added the code Malik mentioned to the @IBaction

     @IBAction func mainButton(sender: CustomBtn, forEvent event: UIEvent){
    
        let touches = event.touches(for: sender)
        let touch = touches?.first
        guard let touchPoint = touch?.location(in: sender) else {return}
        let alphaValue = alphaFromPoint(sender: sender, point: touchPoint)
    
        if alphaValue > 0 {
          //Do Something if the alpha value is bigger than 0 
       }
    }
    

    If the value returned from the alphaValue variable is 0 then you know its transparent.