UIProgressView progressTintColor bug

Hello,


in my current iOS Project I change the UIProgressView tintColor at runtime.

When I use the same color it works great, but if I use 2 different colors e.g.

"progress is less than 20 percent it becomes red else it stays in init color" it does not work.


The bug is that when I change at runtime the progressTintColor the progress bar becomes rounded corners and the actual progress does not match the current progress value. As I discribed by using the same logic and do not use a different color than the initial one it works.


Is this a known bug in iOS?


Code example:


func setBatteryProgress(value: Int) {
        batteryProgress.progress = Float(value) / 100
        if (batteryProgress.progress <= 0.2) {
            self.batteryProgress.progressTintColor = UIColor.red
           //when I use here same color as in the else block it works
        } else {
            batteryProgress.progressTintColor = primaryColor
           //when I use here same color as in the if block it works
        }
}


Strange behavior -> the progress is at a random value, and the left corner of the progress bar is rounded.


Some has an idea about this?


Greetings


Tobias

Replies

I created a viewController, with a UIProgressView, a UITextField to set the value. The textField Editing Did End and Did End on exit are both connected to the IBAction.

I use setProgress to set the value, not directly the property (I think this is what makes the difference)

Here the complete code. Tested, it works.


class SecondViewController: UIViewController {

    @IBOutlet weak var progressIndicator: UIProgressView!
   
    @IBOutlet weak var progressField: UITextField!
   
    override func viewDidLoad() {
        super.viewDidLoad()
    }
   
    @IBAction func setProgressValue(_ sender: UITextField) {
       
        if let value = Int(sender.text!) {
            setBatteryProgress(value: value)
        }
    }
   
    func setBatteryProgress(value: Int) {
       // NOT USED  progressIndicator.progress = Float(value) / 100
        progressIndicator.setProgress(Float(value) / 100, animated: true)
        if (progressIndicator.progress <= 0.2) {
            self.progressIndicator.progressTintColor = UIColor.red
            //when I use here same color as in the else block it works
        } else {
            progressIndicator.progressTintColor = .blue // primaryColor
            //when I use here same color as in the if block it works
        }
    }

}

I tried your code and worked as expected.


Are you sure you call your `setBatteryProgress(value:)` only in the main thread?

@OOPer

I am not sure since the battery progress change value is triggered from a notification center - send/triggered by a singleton.

But I also tried

DispatchQueue.main.async...()


Even if I set the progress via value property not setProgress(), it works with same colors!

This is the strange behavior in my case.


It is triggered via notification like this:

    // Notification function called each time BLEDevice gets characteristc update
    @objc func voltageUpdate(_ notificaiton:Notification) {
        setBatteryProgress(value: BLEDevice.shared.voltage)
        setBatteryText(value: BLEDevice.shared.voltage)
    }



EDIT: But I will try with setProgress asap.

Any notification triggered in a non-main thread will invoke the notification handler in the non-main thread.

So, even if it does not solve all the issues, you should better think using `DispatchQueue.main.async {}` is mandatory.


    @objc func voltageUpdate(_ notificaiton:Notification) {
        DispatchQueue.main.async {
            self.setBatteryProgress(value: BLEDevice.shared.voltage)
            self.setBatteryText(value: BLEDevice.shared.voltage)
        }
    }


The result of updating UI controls in a non-main thread is unpredictable and works with same colors is one possible result.

You may have other parts to fix, but please be sure that all UI updates are done in the main thread.

As I mentioned above I used

DispatchQueue.main.async...() 

at several places in the code (in the notification funciton, in the uiupdate function, tested a lot) but still no success.


Found a thread online seems to be not resolved yet:

https://stackoverflow.com/questions/44081967/calling-progresstintcolor-does-not-update-uiprogressview-progress-between-0-01-a


But thank you for help OOPer, I think it has to be something with the UI update and main thread sync.

Tested: it works in the sample code I attached, with

progressIndicator.progress = Float(value) / 100


even if you loose the animation.

In a static scenario or the same VC it seems to work. When the UI update comes from notification center it hangs up like I described. It is a thread problem I guess. Since I tried DispatchQueue.main.async() in several code spots it has no effect.

Tried a lot of stuff now. It is not working with my intention. Have to find a workaround. Thanks for helping.


Or could it be something with the constrains I set on the UIProgress View? When changing color rgb?


EDIT:


I did an Outlet Button in the same VC - onClick change progress tint color.

Result:

It is changing the color but the progress has now a left rounded corner as I described above.

Could you post the complete code of the VC where the progressView is ?

You say you used `DispatchQueue.main.async...(}`, but you have never shown the exact code.

Why do you need to hide it? It is very likely you are misusing it.


Please show all relevant codes, UI update, any action methods related, any notification setups.

I came a step ahead by changing in the storyboard attribute inspector -> Progress View -> Style (here is set "Bar" now).

Result: the left rounded corner of the progress is fixed now when changing the color of the progress.


But by changing the color, the progress still is always set to a default "buggy value" by about 80 percent even log shows (my current value is correct)


I also set in the storyboard force from left-to-right!

@IBOutlet var batteryProgress: UIProgressView!


in ViewDidLoad:


setBatteryProgress(value: BLEDevice.shared.voltage)
nc.addObserver(self, selector: #selector(voltageDidUpdate(_:)), name: Notification.Name("VoltageUpdate"), object: nil)

Custom functions:


    // Notification function called each time BLEDevice gets characteristc update
    @objc func voltageDidUpdate(_ notificaiton:Notification) {
        // Make sure that it is Akku Mode
        if(BLEDevice.shared.pwrSupplyState == 1) {
            setBatteryProgress(value: BLEDevice.shared.voltage)
        }
    }

    func setBatteryProgress(value: Int) {
        batteryProgress.progress = Float(value) / 100
        if (batteryProgress.progress <= 0.2) {
            batteryProgress.progressTintColor = UIColor.red
        } else {
            batteryProgress.progressTintColor = primaryColor
        }
    }


Fired test scenario from a different ViewController:


   BLEDevice.shared.voltage = 5
   nc.post(name: Notification.Name("VoltageUpdate"), object: nil)


On loading first time (viewDidLoad) BLEDevice.shared.voltage value is 99 and shown correctly!

When the test notification is triggered, the color changes but the progress is not set to 5% as it should be (the log shows it correctly 5%) it hangs up like 0.75 percentage progress.


Last information I could share is that in the storyboard the progress TintColor is set to primary color. By launching my VC the color is first time "overwritten" (with the same color as in storyboard set) by


batteryProgress.progressTintColor = primaryColor


if I set there on first time (ViewDidLoad -> UIColor.red it is also shown correctly. Just on updating it, it buggs.


The left corner bug is fixed as I described above by setting it to "Bar".

Did you try, as avvised by OOPer


    func setBatteryProgress(value: Int) {
       DispatchQueue.main.async {
             self.batteryProgress.progress = Float(value) / 100
             if (self.batteryProgress.progress <= 0.2) {
                 self.batteryProgress.progressTintColor = UIColor.red
             } else {
                 self.batteryProgress.progressTintColor = primaryColor
             }
      }
    }

Yes I really did!

You said :

Or could it be something with the constrains I set on the UIProgress View? When changing color rgb?


What are those constraints ?

How are they linked to color change ?


Did you try disabling all constraints, just to check ?


Another point I did not understand from your setup:


- you create the observer when viewDidLoad

- So when you post from the other view, observer does not exist yet, so the view dioes not receive notification.

Not sure how, but I fixed the same issue by using "tintColor" instead of "progressTintColor".