Home > Software design >  Is there a way to make a completely transparent UIButton that works?
Is there a way to make a completely transparent UIButton that works?

Time:06-04

I've a custom UINavigationItem title view. It has a label pushed to the top of it, which I've enabled to respond to .touchDown and send an action.

However, the taps don't register near the top of the label, presumably because the active region is clipped. So I configured another invisible view (not in navigation item), and set it up as a control, and positioned it above that navigation title view label.

However, it doesn't work unless I set the 'invisible' view's alpha to at least 0.02, because Apple seems to intentionally disable action for a view with an alpha less than that. Unfortunately, against a black screen in dark mode, the 'invisible' hitpad view shows up as a slightly grey rectangle, which is not a good aesthetic.

I guess I could go to some lengths to try to make the button background color match the screen background at that location, but it seems a bit tacky.

What alternatives might I have?

CodePudding user response:

You can simply create a blank png image, and add it in top of your title view. make sure to set the imageView and the title view isUserInteractionEnabled properties to true:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .green
        let imageView = UIImageView()
        imageView.isUserInteractionEnabled = true
        let tap = UITapGestureRecognizer(
            target: self,
            action: #selector(tap)
        )
        imageView.addGestureRecognizer(tap)
        imageView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        imageView.image = UIImage(named: "blank")
        let titleLabel = UILabel()
        titleLabel.text = "Transparent Button"
        titleLabel.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        titleLabel.addSubview(imageView)
        navigationItem.titleView = titleLabel
        navigationItem.titleView?.isUserInteractionEnabled = true
    }
    @objc func tap(_ gesture: UITapGestureRecognizer) {
        print(#function)
    }
}

Sample project


You can also just add your gesture recognizer directly to your titleView. No need for the transparent image at all unless you need to control the area of the gesture:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .green
        let tap = UITapGestureRecognizer(
            target: self,
            action: #selector(tap)
        )
        let titleLabel = UILabel()
        titleLabel.text = "Transparent Button"
        titleLabel.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        titleLabel.addGestureRecognizer(tap)
        navigationItem.titleView = titleLabel
        navigationItem.titleView?.isUserInteractionEnabled = true
    }
    @objc func tap(_ gesture: UITapGestureRecognizer) {
        print(#function)
    }
}

CodePudding user response:

This is an adaptation of the @LeoDabus' Accepted answer that works. However it was utterly informed by his explanation and example. The only meaningful change I made to Leo's example was to create a real empty image programmatically, and drop the label generation. Without a real empty UIImage(), the only way to make taps on the region work that I found is to set the image view's background color to non-clear.

func emptyImage(with size: CGSize) -> UIImage?
{
    UIGraphicsBeginImageContext(size)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

func configureButtons() {
    let imageView = UIImageView(image: emptyImage(with: CGSize(width: view.frame.size.width - 250, height: 44)))
    imageView.frame = CGRect(x: 140, y: self.view.safeAreaInsets.top   50,
                             width: view.frame.size.width - 250, height: 44)
    imageView.isUserInteractionEnabled = true
    let tap = UITapGestureRecognizer(target: self, action: #selector(actionEnableTitleEditing))
    imageView.addGestureRecognizer(tap)
    imageView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    view.addSubview(imageView)
}
  • Related