简体   繁体   中英

How to automatically change transparent color of a view based on the color of it’s background

I want to display a semi transparent text field over the uiimageview. I can't choose static color for textfield because some images are bright while other images are dark. I want to automatically adapt text field color based on the colors behind it. Is there an easy solution for that?

UPD: The effect I want to achieve is: If UIImage is dark where my textfield should be placed, set textfield background color to white with 0.5 opacity. If UIImage is light where my textfield should be placed, set textfield background color to black with 0.5 opacity.

So I want to somehow calculate the average color of the uiimageview in place where I want to put my textfield and then check if it is light or dark. I don't know, how to get the screenshot of the particular part my uiimageview and get it's average color. I want it to be optimised. I guess working with UIGraphicsImageRenderer isn't a good choice, that's why I ask this question. I know how to do it with UIGraphicsImageRenderer, but I don't think that my way is good enough.

"Brightness" of an image is not a straight-forward thing. You may or may not find this suitable.

If you search for determine brightness of an image you'll find plenty of documentation on it - likely way more than you want.

One common way to calculate the "brightness" of a pixel is to use the sum of:

red component   * 0.299
green component * 0.587
blue component  * 0.114

This is because we perceive the different colors at different "brightness" levels.

So, you'll want to loop through each pixel in the area of the image where you want to place your label (or textField), get the average brightness, and then decide what's "dark" and what's "light".

As an example, using this background image:

在此处输入图像描述

I generated a 5 x 8 grid of labels, looped through getting the "brightness" of the image in the rect under each label's frame, and then set the background and text color based on the brightness calculation (values range from 0 to 255, so I used < 127 is dark, >= 127 is light):

在此处输入图像描述

This is the code I used:

extension CGImage {
    var brightness: Double {
        get {
            // common formula to get "average brightness"
            let bytesPerPixel = self.bitsPerPixel / self.bitsPerComponent
            let imageData = self.dataProvider?.data
            let ptr = CFDataGetBytePtr(imageData)
            var x = 0
            var p = 0
            var result: Double = 0
            for _ in 0..<self.height {
                for _ in 0..<self.width {
                    let r = ptr![p+0]
                    let g = ptr![p+1]
                    let b = ptr![p+2]
                    result += (0.299 * Double(r) + 0.587 * Double(g) + 0.114 * Double(b))
                    p += bytesPerPixel
                    x += 1
                }
            }
            let bright = result / Double (x)
            return bright
        }
    }
}
extension UIImage {
    // get the "brightness" of self (entire image)
    var brightness: Double {
        get {
            return (self.cgImage?.brightness)!
        }
    }

    // get the "brightness" in a sub-rect of self
    func brightnessIn(_ rect: CGRect) -> Double {
        guard let cgImage = self.cgImage else { return 0.0 }
        guard let croppedCGImage = cgImage.cropping(to: rect) else { return 0.0 }
        return croppedCGImage.brightness
    }
}

class ImageBrightnessViewController: UIViewController {
    
    let imgView: UIImageView = {
        let v = UIImageView()
        v.contentMode = .center
        v.backgroundColor = .green
        v.clipsToBounds = true
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    var labels: [UILabel] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // load an image
        guard let img = UIImage(named: "bkg640x360") else { return }

        imgView.image = img

        let w = img.size.width
        let h = img.size.height
        
        // set image view's width and height equal to img width and height
        view.addSubview(imgView)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            imgView.widthAnchor.constraint(equalToConstant: w),
            imgView.heightAnchor.constraint(equalToConstant: h),
            imgView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            imgView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
        ])

        // use stack views to create a 5 x 8 grid of labels
        let outerStackView: UIStackView = {
            let v = UIStackView()
            v.translatesAutoresizingMaskIntoConstraints = false
            v.axis = .horizontal
            v.spacing = 32
            v.distribution = .fillEqually
            return v
        }()

        for _ in 1...5 {
            let vStack = UIStackView()
            vStack.axis = .vertical
            vStack.spacing = 12
            vStack.distribution = .fillEqually
            for _ in 1...8 {
                let label = UILabel()
                label.textAlignment = .center
                vStack.addArrangedSubview(label)
                labels.append(label)
            }
            outerStackView.addArrangedSubview(vStack)
        }
        
        let padding: CGFloat = 12.0

        imgView.addSubview(outerStackView)
        NSLayoutConstraint.activate([
            outerStackView.topAnchor.constraint(equalTo: imgView.topAnchor, constant: padding),
            outerStackView.leadingAnchor.constraint(equalTo: imgView.leadingAnchor, constant: padding),
            outerStackView.trailingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: -padding),
            outerStackView.bottomAnchor.constraint(equalTo: imgView.bottomAnchor, constant: -padding),
        ])
        
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        guard let img = imgView.image else {
            return
        }
        labels.forEach { v in
            if let sv = v.superview {
                // convert label frame to imgView coordinate space
                let rect = sv.convert(v.frame, to: imgView)
                // get the "brightness" of that rect from the image
                //  it will be in the range of 0 - 255
                let d = img.brightnessIn(rect)
                // set the text of the label to that value
                v.text = String(format: "%.2f", d)
                // just using 50% here... adjust as desired
                if d > 127.0 {
                    // if brightness is than 50%
                    //  black translucent background with white text
                    v.backgroundColor = UIColor.black.withAlphaComponent(0.5)
                    v.textColor = .white
                } else {
                    // if brightness is greater than or equal to 50%
                    //  white translucent background with black text
                    v.backgroundColor = UIColor.white.withAlphaComponent(0.5)
                    v.textColor = .black
                }
            }
        }
    }
}

As you can see, when getting the average of a region of a photo, you'll quite often not be completely happy with the result. That's why it's much more common to see one or the other, with a contrasting order and either a shadow or glow around the frame.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM