如何根据背景颜色自动更改视图的透明颜色
How to automatically change transparent color of a view based on the color of it’s background
我想在 uiimageview 上显示一个半透明的文本字段。我不能为文本字段选择静态颜色,因为有些图像很亮,而另一些图像很暗。我想根据其背后的颜色自动调整文本字段颜色。有没有简单的解决方案?
更新:
我想要达到的效果是:
如果 UIImage 在我的文本字段应该放置的地方是黑暗的,将文本字段背景颜色设置为白色,不透明度为 0.5。
如果 UIImage 在我的文本字段应该放置的地方很亮,将文本字段背景颜色设置为黑色,不透明度为 0.5。
所以我想以某种方式计算 uiimageview 在我想放置文本字段的位置的平均颜色,然后检查它是浅色还是深色。我不知道,如何获取我的 uiimageview 特定部分的屏幕截图并获取它的平均颜色。我希望它被优化。我想使用 UIGraphicsImageRenderer 不是一个好的选择,这就是我问这个问题的原因。我知道如何使用 UIGraphicsImageRenderer 来实现,但我认为我的方法不够好。
图像的“亮度”并不是一件简单的事情。您可能会或可能不会找到合适的。
如果您搜索 determine brightness of an image
,您会找到大量关于它的文档 - 可能比您想要的多得多。
计算像素“亮度”的一种常用方法是使用以下总和:
red component * 0.299
green component * 0.587
blue component * 0.114
这是因为我们在不同的“亮度”级别感知不同的颜色。
因此,您需要遍历图像区域中要放置标签(或文本字段)的每个像素,获取平均亮度,然后决定什么是“暗”,什么是“亮” ".
例如,使用此背景图片:
我生成了一个 5 x 8 的标签网格,循环获取每个标签框架下矩形中图像的“亮度”,然后根据亮度计算设置背景和文本颜色(值范围从0 到 255,所以我用 < 127 是暗的,>= 127 是亮的):
这是我使用的代码:
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
}
}
}
}
}
如您所见,在获取照片某个区域的平均值时,您通常不会对结果完全满意。这就是为什么更常见的是看到一个或另一个,具有对比顺序并且框架周围有阴影或发光。
我想在 uiimageview 上显示一个半透明的文本字段。我不能为文本字段选择静态颜色,因为有些图像很亮,而另一些图像很暗。我想根据其背后的颜色自动调整文本字段颜色。有没有简单的解决方案?
更新: 我想要达到的效果是: 如果 UIImage 在我的文本字段应该放置的地方是黑暗的,将文本字段背景颜色设置为白色,不透明度为 0.5。 如果 UIImage 在我的文本字段应该放置的地方很亮,将文本字段背景颜色设置为黑色,不透明度为 0.5。
所以我想以某种方式计算 uiimageview 在我想放置文本字段的位置的平均颜色,然后检查它是浅色还是深色。我不知道,如何获取我的 uiimageview 特定部分的屏幕截图并获取它的平均颜色。我希望它被优化。我想使用 UIGraphicsImageRenderer 不是一个好的选择,这就是我问这个问题的原因。我知道如何使用 UIGraphicsImageRenderer 来实现,但我认为我的方法不够好。
图像的“亮度”并不是一件简单的事情。您可能会或可能不会找到合适的。
如果您搜索 determine brightness of an image
,您会找到大量关于它的文档 - 可能比您想要的多得多。
计算像素“亮度”的一种常用方法是使用以下总和:
red component * 0.299
green component * 0.587
blue component * 0.114
这是因为我们在不同的“亮度”级别感知不同的颜色。
因此,您需要遍历图像区域中要放置标签(或文本字段)的每个像素,获取平均亮度,然后决定什么是“暗”,什么是“亮” ".
例如,使用此背景图片:
我生成了一个 5 x 8 的标签网格,循环获取每个标签框架下矩形中图像的“亮度”,然后根据亮度计算设置背景和文本颜色(值范围从0 到 255,所以我用 < 127 是暗的,>= 127 是亮的):
这是我使用的代码:
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
}
}
}
}
}
如您所见,在获取照片某个区域的平均值时,您通常不会对结果完全满意。这就是为什么更常见的是看到一个或另一个,具有对比顺序并且框架周围有阴影或发光。