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

738 Views Asked by At

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)
}
2

There are 2 best solutions below

0
Lance Samaria On BEST ANSWER

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
}
3
Abdelahad Darwish On

you have to add UILabel to UIVisualEffectView

 backgroundBlur.addSubview(labelOne)
 backgroundBlur.addSubview(labelTwo)
  1. add backgroundBlur and set constraint

  2. then add labelOne,labelTwo to backgroundBlur with there constraint

    Or Add all to UIView and connect them with constraint

    don't forget translatesAutoresizingMaskIntoConstraints = false

Declaration:

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

    let labelOne: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    let labelTwo: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

ViewDidLoad

self.view.addSubview(backgroundBlur)
self.view.addSubview(labelOne)
self.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

labelOne.text = "asdhaskdas saldhlsahdsa laskdhsakhd asdlhkhsad"
labelTwo.text = "asdhaskdas saldhlsahdsa laskdhsakhd asdlhkhsad"


labelOne.numberOfLines = 0
labelTwo.numberOfLines = 0

backgroundBlur.topAnchor.constraint(equalTo: labelOne.topAnchor, constant: -8).isActive = true
backgroundBlur.bottomAnchor.constraint(equalTo: labelTwo.bottomAnchor, constant: 8).isActive = true
backgroundBlur.trailingAnchor.constraint(equalTo: labelOne.trailingAnchor, constant: 8).isActive = true
backgroundBlur.leadingAnchor.constraint(equalTo: labelOne.leadingAnchor, constant: -8).isActive = true