我有這個UIImageView,我只改變影像的白色。當白色改變時,它不會再次改變,因為白色不再是白色。每次按下按鈕時,我都想訪問新顏色并將其更改為不同的顏色。我正在使用我在 github 上找到的這個函式。
var currentColor = UIColor.init(red: 1, green: 1, blue: 1, alpha: 1)
@IBAction func changeColors(_ sender: Any) {
let randomRGB = CGFloat.random(in: 0.0...1.0)
let randomRGB2 = CGFloat.random(in: 0.0...1.0)
let randomRGB3 = CGFloat.random(in: 0.0...1.0)
//randomly change color
var newColor = UIColor.init(red: randomRGB3, green: randomRGB2, blue: randomRGB, alpha: 1)
let changeColor = replaceColor(color: currentColor, withColor: newColor, image: mainImage.image!, tolerance: 0.5)
mainImage.image = changeColor
//change current color to new color
currentColor = newColor
}
extension ViewController {
func replaceColor(color: UIColor, withColor: UIColor, image: UIImage, tolerance: CGFloat) -> UIImage {
// This function expects to get source color(color which is supposed to be replaced)
// and target color in RGBA color space, hence we expect to get 4 color components: r, g, b, a
assert(color.cgColor.numberOfComponents == 4 && withColor.cgColor.numberOfComponents == 4,
"Must be RGBA colorspace")
// Allocate bitmap in memory with the same width and size as source image
let imageRef = image.cgImage!
let width = imageRef.width
let height = imageRef.height
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width;
let bitsPerComponent = 8
let bitmapByteCount = bytesPerRow * height
let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: bitmapByteCount)
let context = CGContext(data: rawData, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: CGColorSpace(name: CGColorSpace.genericRGBLinear)!,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue)
let rc = CGRect(x: 0, y: 0, width: width, height: height)
// Draw source image on created context
context!.draw(imageRef, in: rc)
// Get color components from replacement color
let withColorComponents = withColor.cgColor.components
let r2 = UInt8(withColorComponents![0] * 255)
let g2 = UInt8(withColorComponents![1] * 255)
let b2 = UInt8(withColorComponents![2] * 255)
let a2 = UInt8(withColorComponents![3] * 255)
// Prepare to iterate over image pixels
var byteIndex = 0
while byteIndex < bitmapByteCount {
// Get color of current pixel
let red = CGFloat(rawData[byteIndex 0]) / 255
let green = CGFloat(rawData[byteIndex 1]) / 255
let blue = CGFloat(rawData[byteIndex 2]) / 255
let alpha = CGFloat(rawData[byteIndex 3]) / 255
let currentColor = UIColor(red: red, green: green, blue: blue, alpha: alpha)
// Compare two colors using given tolerance value
if compareColor(color: color, withColor: currentColor , withTolerance: tolerance) {
// If the're 'similar', then replace pixel color with given target color
rawData[byteIndex 0] = r2
rawData[byteIndex 1] = g2
rawData[byteIndex 2] = b2
rawData[byteIndex 3] = a2
}
byteIndex = byteIndex 4;
}
// Retrieve image from memory context
let imgref = context!.makeImage()
let result = UIImage(cgImage: imgref!)
// Clean up a bit
rawData.deallocate()
return result
}
func compareColor(color: UIColor, withColor: UIColor, withTolerance: CGFloat) -> Bool
{
var r1: CGFloat = 0.0, g1: CGFloat = 0.0, b1: CGFloat = 0.0, a1: CGFloat = 0.0;
var r2: CGFloat = 0.0, g2: CGFloat = 0.0, b2: CGFloat = 0.0, a2: CGFloat = 0.0;
color.getRed(&r1, green: &g1, blue: &b1, alpha: &a1);
withColor.getRed(&r2, green: &g2, blue: &b2, alpha: &a2);
return abs(r1 - r2) <= withTolerance &&
abs(g1 - g2) <= withTolerance &&
abs(b1 - b2) <= withTolerance &&
abs(a1 - a2) <= withTolerance;
}
}
uj5u.com熱心網友回復:
以下是我所做的一些觀察,可能會影響您看到的結果:
正如我們在評論中討論的那樣,如果您想從之前更改的顏色開始,您需要在影像更新后保持顏色超出您的功能范圍(您這樣做了)
關于以一種顏色結束的下一個問題可能與容錯有很大關系
- 當您嘗試以給定顏色的 0.5 (50%) 容錯度更改影像中的顏色時,您將在第一遍中更改影像中的大量顏色
- 如果顏色系統中有 100 種顏色,您將在影像中查找其中的 50 種顏色并將它們更改為 1 種特定顏色
- 在第一遍中,您從白色開始。假設 75% 的影像具有與白色相似的顏色,具有 50% 的容錯能力 - 75% 的影像將變為該顏色
- 具有如此高的容錯性,很快就會出現一種顏色,該顏色將接近影像中具有 50% 容錯能力的大多數顏色,最終您將獲得具有 1 個影像的顏色
改善結果的一些想法
- 設定較低的容錯 - 你會看到更小的變化,同樣的結果可能會出現在 1 種顏色上,但它會在更長的時間內發生
- 如果您真的想隨機化并且沒有得到這 1 種顏色的結果,我建議您更改使用
currentColor方式并對原始影像進行更改,而不是更新后的影像(我在下面有這個示例) - 這不會影響解決方案,但更好地處理選項更安全,因為我看到了很多
!所以我建議改變它 - 在后臺執行緒中執行影像處理(也在下面的示例中)
這是一個帶有示例的更新
class ImageColorVC: UIViewController
{
// UI Related
private var loaderController: UIAlertController!
let mainImage = UIImageView(image: UIImage(named: "art"))
// Save the current color and the original image
var currentColor = UIColor.init(red: 1, green: 1, blue: 1, alpha: 1)
var originalImage: UIImage!
override func viewDidLoad()
{
super.viewDidLoad()
// UI configuration, you can ignore
view.backgroundColor = .white
title = "Image Color"
configureBarButton()
configureImageView()
// Store the original image
originalImage = mainImage.image!
}
// MARK: AUTO LAYOUT
private func configureBarButton()
{
let barButton = UIBarButtonItem(barButtonSystemItem: .refresh,
target: self,
action: #selector(changeColors))
navigationItem.rightBarButtonItem = barButton
}
private func configureImageView()
{
mainImage.translatesAutoresizingMaskIntoConstraints = false
mainImage.contentMode = .scaleAspectFit
mainImage.clipsToBounds = true
view.addSubview(mainImage)
view.addConstraints([
mainImage.leadingAnchor
.constraint(equalTo: view.leadingAnchor),
mainImage.topAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
mainImage.trailingAnchor
.constraint(equalTo: view.trailingAnchor),
mainImage.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
// Configures a loader to show while image is processing
private func configureLoaderController()
{
loaderController = UIAlertController(title: nil,
message: "Processing",
preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10,
y: 5,
width: 50,
height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.medium
loadingIndicator.startAnimating();
loaderController.view.addSubview(loadingIndicator)
}
//MARK: FACTORY
// Similar to your function, only difference is that it uses
// the original image
private func performChangeColors()
{
let randomRGB = CGFloat.random(in: 0.0...1.0)
let randomRGB2 = CGFloat.random(in: 0.0...1.0)
let randomRGB3 = CGFloat.random(in: 0.0...1.0)
//randomly change color
let newColor = UIColor.init(red: randomRGB3,
green: randomRGB2,
blue: randomRGB,
alpha: 1)
// Do work in the back ground
DispatchQueue.global(qos: .background).async
{
let imageWithNewColor = self.replaceColor(color: self.currentColor,
withColor: newColor,
image: self.originalImage!,
tolerance: 0.5)
// Update the UI on the main thread
DispatchQueue.main.async
{
self.updateImageView(with: imageWithNewColor)
//change current color to new color
self.currentColor = newColor
}
}
}
@objc
private func changeColors()
{
// Configure a loader to show while image is processing
configureLoaderController()
present(loaderController, animated: true) { [weak self] in
self?.performChangeColors()
}
}
// Update the UI
private func updateImageView(with image: UIImage)
{
dismiss(animated: true) { [weak self] in
self?.mainImage.image = image
}
}
}
從這個開始之后:

大約 50 次嘗試后,它似乎仍然運行良好:

您可以在此處觀看更長的視頻,以查看發生的更多顏色變化而不會導致單一顏色
希望這足以為您的解決方案創建所需的解決方法
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/431508.html
標籤:迅速 代码 uiimageview uicolor
