Search code examples
iosswiftuilabelsubviewsuivisualeffectview

How to send UIVisualEffectView behind UILabel when based on the label's text's width and height


I added a blur effect behind a label. The blur refuses to go behind the label. I tried all 3 of these separately:

label.insertSubview(backgroundBlur, at: 0)
label.addSubview(backgroundBlur)
label.sendSubview(toBack: backgroundBlur)

The thing is I need the width and height of the UIVisualEffectView blur to be based on the the size of the label's text. The text is dynamic. Both labels need to have their own individual backgroundBlur.

How can I get the UIVisualEffectView blur to go behind each indivdual label when it's also based on the label's text's width and height? There should be two labels with 2 backgroundBlurs behind them.

let backgroundBlur: UIVisualEffectView = {
    let blur = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.dark))
    blur.layer.cornerRadius = 6
    blur.layer.masksToBounds = true
    return blur
}()

let labelOne: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.boldSystemFont(ofSize: 17)
    label.textColor = UIColor.white
    label.textAlignment = .center
    label.sizeToFit()
    label.numberOfLines = 0
    return label
}()

let labelTwo: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.boldSystemFont(ofSize: 17)
    label.textColor = UIColor.white
    label.textAlignment = .center
    label.sizeToFit()
    label.numberOfLines = 0
    return label
}()

override func viewDidLoad() {
    super.viewDidLoad()

    view.addSubview(labelOne)
    view.addSubview(labelTwo)

    labelOne.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    labelOne.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    labelTwo.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 16).isActive = true

    putBlurEffectBehindLabel(backgroundBlur, labelOne)

    putBlurEffectBehindLabel(backgroundBlur, labelTwo)
}

func putBlurEffectBehindLabel(_ blur: UIVisualEffectView, _ label: UILabel){
    blur.frame = label.bounds

    // tried these individually but nada
    label.insertSubview(backgroundBlur, at: 0) 
    label.addSubview(backgroundBlur)
    label.sendSubview(toBack: backgroundBlur)

    blur.center = CGPoint(x: label.bounds.midX, y: label.bounds.midY)
}

Solution

  • I had to add two UIVisualEffectView named:

    backgroundBlurOne
    backgroundBlurTwo
    

    Based on @AbdelahadDarwish suggestion of adding the label to the blur instead of adding the blur to the label I was able to get the blur behind the label:

    // this is added inside the putBlurEffectBehindLabel function
    blur.contentView.addSubview(label)
    

    Also inside the putBlurEffectBehindLabel function I got the size of the label's text using (text! as NSString).size(withAttributes: [NSAttributedStringKey.font: UIFont]) and then based the width and height of the UIVisualEffectView (the blur) off of that.

    I then added the text I wanted for labelOne and backgroundBlurOne in viewDidLoad.

    Then I added the text I wanted for labelTwo and the backgroundBlurTwo for it in viewDidAppear. I had to do that so I can use the height from backgroundBlurOne + a 16 point distance so labelTwo could be where I needed it to be from labelOne.

    let backgroundBlurOne: UIVisualEffectView = {
        let blur = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.dark))
        blur.translatesAutoresizingMaskIntoConstraints = false
        blur.layer.cornerRadius = 6
        blur.layer.masksToBounds = true
        blur.isUserInteractionEnabled = false
        blur.backgroundColor = UIColor.black.withAlphaComponent(10)
        return blur
    }()
    
    let backgroundBlurTwo: UIVisualEffectView = {
        let blur = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.dark))
        blur.translatesAutoresizingMaskIntoConstraints = false
        blur.layer.cornerRadius = 6
        blur.layer.masksToBounds = true
        blur.isUserInteractionEnabled = false
        blur.backgroundColor = UIColor.black.withAlphaComponent(10)
        return blur
    }()
    
    let labelOne: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 17)
        label.textColor = UIColor.white
        label.textAlignment = .center
        label.sizeToFit()
        label.numberOfLines = 0
        return label
    }()
    
    let labelTwo: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 17)
        label.textColor = UIColor.white
        label.textAlignment = .center
        label.sizeToFit()
        label.numberOfLines = 0
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        labelOne.text = "Hello"
        putBlurEffectBehindLabel(backgroundBlurOne, labelOne, yDistance: 0)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        labelTwo.text = "Heloooooooooooooooooooooooo"
        putBlurEffectBehindLabel(backgroundBlurTwo, labelTwo, yDistance: backgroundBlurOne.frame.height + 16)
    }
    
    func putBlurEffectBehindLabel(_ blur: UIVisualEffectView, _ label: UILabel, yDistance: CGFloat){
    
        view.addSubview(blur)
        blur.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        blur.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: yDistance).isActive = true
    
        blur.contentView.addSubview(label)
        label.centerXAnchor.constraint(equalTo: blur.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: blur.centerYAnchor).isActive = true
    
        let text = label.text
        let textSize = (text! as NSString).size(withAttributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17)])
    
        blur.widthAnchor.constraint(equalToConstant: textSize.width + 15).isActive = true
        blur.heightAnchor.constraint(equalToConstant: textSize.height + 10).isActive = true
    }