Why does UIProgressView not update? - ios

I'm trying to set the progress of a UIProgressView, but the progressView doesn't "update".
Why? Do you know what I am doing wrong?
Here is my code in ViewController.h:
IBOutlet UIProgressView *progressBar;
That's the code in ViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
progressBar.progress = 0.0;
[self performSelectorOnMainThread:#selector(progressUpdate) withObject:nil waitUntilDone:NO];
}
-(void)progressUpdate {
float actual = [progressBar progress];
if (actual < 1) {
progressBar.progress = actual + 0.1;
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self `selector:#selector(progressUpdate) userInfo:nil repeats:NO];`
}
else {
}
}

Try calling [progressBar setProgress:<value> animated:YES]; instead of progressBar.progress = <value>
EDIT: Take a look at this, which looks a lot like your example: Objective c : How to set time and progress of uiprogressview dynamically?

Double check that progressBar is linked correctly in Interface Builder.
If it still doesn't work, try using [progressBar setProgress:0]; and [progressBar setProgress:actual+0.1];.
Also know that the UIProgressView has a [progressBar setProgress:<value> animated:YES]; method. May look cleaner.

Its should look like this:
- (void)viewDidLoad
{
[super viewDidLoad];
progressBar.progress = 0.0;
[self performSelectorInBackground:#selector(progressUpdate) withObject:nil];
}
-(void)progressUpdate {
for(int i = 0; i<100; i++){
[self performSelectorOnMainThread:#selector(setProgress:) withObject:[NSNumber numberWithFloat:(1/(float)i)] waitUntilDone:YES];
}
}
- (void)setProgress:(NSNumber *)number
{
[progressBar setProgress:number.floatValue animated:YES];
}

I had the same problem this morning. It appeared that the progressBar was partly over a UITableView. I moved it a little, so that it was not overlapping anymore.
I'm not sure why this is. It should have been working in the original case, but that is how I solved it.

Related

UIView animation not working in iOS 7

I am working on a sample project in which I have a vertical scrollview and a horizontal scrollview. The vertical scrollview has a number of subviews. In scrollviewDidScroll I am performing some actions. Along with this I now want to animate a subview inside the vertical scrollview when that particular subview is visible on the screen. For this I am doing a certain calculation which is correct. The animation is as follows:
The subview contains multiple custom views. I am trying to animate(reducing and then again increasing alpha value for the view) each of these views in some particular time sequence(so that the animation looks to be in sequence). For this I am posting notification to the views and the animation sequence and logic is perfect according to what I want.
But I am facing the problem that the code is executing when I put the breakpoint, but the animation does not show up. In case if I post the notification after I stop scrolling then the animation happens perfectly fine. But I want is the animation to happen even when I am scrolling and the view is on screen.
I am adding my code snippet as below:
SubView: (Inside my scrollview)
- (void)animateSequenceTemplate {
if (!hasSequenceTemplateAnimated) {
[self performSelector:#selector(animateSequenceSlides) withObject:nil afterDelay:0];
hasSequenceTemplateAnimated = YES;
}
else {
return;
}
}
- (void)animateSequenceSlides {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:imageAsset forKey:#"assetMO"];
[[NSNotificationCenter defaultCenter]postNotificationName:AnimateSequenceSlide object:nil userInfo:userInfo];
}
Subviews inside the above subview:
- (void)animateSlideView:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
if ([userInfo objectForKey:#"assetMO"] == assetMO) {
[[NSNotificationCenter defaultCenter]removeObserver:self name:AnimateSequenceSlide object:nil];
CGFloat duration = 0.035;
float delay = (self.slideCount * duration);
CGFloat timeDelay = 0;
[self performSelector:#selector(animationPhase1) withObject:nil afterDelay:delay];
timeDelay = delay + duration;
[self performSelector:#selector(animationPhase2) withObject:nil afterDelay:timeDelay];
if (self.slideCount == (self.maxSlideCount - 1)) {
timeDelay += duration * 18; //No animation till 18 frames
}
else {
timeDelay += duration;
}
[self performSelector:#selector(animationPhase3) withObject:nil afterDelay:timeDelay];
timeDelay += duration;
[self performSelector:#selector(animationPhase4) withObject:nil afterDelay:timeDelay];
}
}
- (void)animationPhase1 {
[UIView animateWithDuration:0.035 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ {
[self setAlpha:0.85];
}completion:^(BOOL finished) {
}];
}
There occurs two scenarios inside - (void)animateSequenceTemplate:
I call as [self animateSequenceSlides];. In this case the animation does not show up.
[self performSelector:#selector(animateSequenceSlides) withObject:nil afterDelay:0.0f];. In this case the animation shows up but after the scrollview rests.
I am compelled to use the UIView animation using perform selector because if I remove this and use either nested UIView animate blocks / or directly call these methods, then again animation does not show up. Now at lease it shows up after the I rest scrolling.
I would like advice on a solution or any guess on what mistake I might be making.
It possible, that you executing it not on the main queue.
dispatch_async(dispatch_get_main_queue, block()) might help.
When working with animation, main queue the only place for executing code, that makes animation happen.
EDIT:
[self performSelector:#selector(animationPhase1) withObject:nil afterDelay:delay];
I’m not sure, where it happens, but I don't think on main thread.
Try this:
[self performSelectorOnMainThread:#selector(animationPhase1)
withObject:nil
waitUntilDone:YES];
I’m not sure about delay, that’s why I prefer GCD functions and blocks.
Finally, i got the solution to my question and it is working very fine as expected.
Changes Made:
From "animateSequenceTemplate", calling "animateSequenceSlides"
method directly
In "animateSlideView" method mentioned above, instead of using perform selector I created NSTimer object with the required delay,
repeat set to NO, and adding the timer in the current run loop
Did the same for calling all the four animationPhase methods
Code works fine as accepted
- (void)animateSequenceTemplate {
if (!hasSequenceTemplateAnimated && self.isSequenceSlideCreated) {
hasSequenceTemplateAnimated = YES;
[self animateSequenceSlides];
}
}
- (void)animateSlideView:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
if ([userInfo objectForKey:#"assetMO"] == assetMO) {
[[NSNotificationCenter defaultCenter]removeObserver:self name:AnimateSequenceSlide object:nil];
CGFloat duration = 0.035;
float delay = (self.slideCount * duration);
CGFloat timeDelay = 0;
NSTimer *animate1 = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(animationPhase1) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate1 forMode:NSRunLoopCommonModes];
timeDelay = delay + duration;
NSTimer *animate2 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase2) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate2 forMode:NSRunLoopCommonModes];
if (self.slideCount == (self.maxSlideCount - 1)) {
timeDelay += duration * 18; //No animation till 18 frames
}
else {
timeDelay += duration;
}
NSTimer *animate3 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase3) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate3 forMode:NSRunLoopCommonModes];
timeDelay += duration;
NSTimer *animate4 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase4) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate4 forMode:NSRunLoopCommonModes];
}
}
I am not satisfied with the goodness of the code structure. If you think of any other smart way of doing it, please share.

Updating label text (dynamically) while executing another method

I am an iOS developer currently developing an iphone application. I have a simple question regarding updating the contents of a ViewController and I would greatly appreciate it if I can get someone’s feedback who is aware of this issue or have a suggested solution.
I’m writing a method that constantly updates the text of a label (and other components such as the background colour of a UIImage “box1” and “box2” ).
The issue : update to the text of the label (along with other changes) only takes effect at the end when the code was done execution. So I have written a simple infinite loop that does this as follows:
-(void) stateTest{
int i=0;
for(;;){
if (i==5) {
self.stateLabel.text=#"Opened"; //stateLabel previously declared
[self.box2 setBackgroundColor:[UIColor whiteColor]]; // box2 declared as UIImage
[self.box1 setBackgroundColor:[UIColor purpleColor]];
}
if (i==10){
self.stateLabel.text=#"Closed";
[self.box2 setBackgroundColor:[UIColor redColor]];
[self.box1 setBackgroundColor:[UIColor whiteColor]];
break;
}
i++;
}
}
and in the viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
self.stateLabel.text=#“View did Load";
[self.box1 setBackgroundColor:[UIColorwhiteColor]];
[self.box2 setBackgroundColor:[UIColorwhiteColor]];
[self stateTest];
}
I’m stepping through the code (breakpoints) and this is the issue I’m facing:
when i==5 the label text does NOT get updated (same as the color of the boxes)
when i==10, the label text does NOT get updated
The update (both the label text and the box color) show up after the code is done executing with
stateLabel : "Closed",
Box 2: red background color
I have tried calling upon several functions at the end of the stateTest method (after the i++) hoping that it will “REFRESH” the contents of the view, label and/or the UIImage:
[self.stateLabel setNeedsLayout];
[self.stateLabel setNeedsDisplay];
[self.box1 setNeedsDisplay];
[self.box2 setNeedsDisplay];
[self.view setNeedsDisplay];
Unfortunately non of these trials worked. I have also put an NSLog that outputs the text of the label and that works just fine, as I want. But the problem is with the dynamic update of the contents of the view controller during execution of code/method.
I would greatly appreciate any help and I am open to any suggestion
Ideally, this code is used in parallel with a face detection algorithm that detects the state of the mouth. There is a processImage method that processes the image every frame. I call [self stateTest] every time the image is processed; if the mouth is opened → stateLabel.text=#”Open”…etc
Thank you in advance for your contribution
Cheers!
The UI update will happen only when the run loop is completed. normally i run the infinite loop in background thread and post the UI update in the main thread. This will take care of the run loop.
Below code is for reference.
-(void) viewDidLoad
{
[super viewDidLoad];
self.subView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 30, 30)];
self.subView.backgroundColor = [UIColor blueColor];
[self.view addSubview:self.subView];
dispatch_queue_t newQueue = dispatch_queue_create("newQueue", nil);
dispatch_async(newQueue, ^{
[self stateTest];
});
}
-(void) stateTest{
int i=0;
for(;;){
if (i==5) {
[self performSelectorOnMainThread:#selector(purpleColor) withObject:self waitUntilDone:NO];
}
if (i==10){
[self performSelectorOnMainThread:#selector(blueColor) withObject:self waitUntilDone:NO];
// break;
}
if (i==15){
[self performSelectorOnMainThread:#selector(redColor) withObject:self waitUntilDone:NO];
//break;
}
if (i==20){
[self performSelectorOnMainThread:#selector(greenColor) withObject:self waitUntilDone:NO];
break;
}
sleep(1);
i++;
}
}
-(void) purpleColor
{
[self.subView setBackgroundColor:[UIColor purpleColor]];
}
-(void) blueColor
{
[self.subView setBackgroundColor:[UIColor blueColor]];
}
-(void) redColor
{
[self.subView setBackgroundColor:[UIColor redColor]];
}
-(void) greenColor
{
[self.subView setBackgroundColor:[UIColor greenColor]];
}
it's not a very good idea to run a infinite loop on the main thread (uithread). Instead can I suggest you to use a timer or a dispatch event ? below is some code you can use for a timer. and I would also point you out that NSTimer cannot be re used. hence if you wish to do this operation after you invalidated it you need to re create the timer & add it into a runloop.
// properties in your controller
#property (nonatomic) BOOL state;
#property (nonatomic, strong) NSTimer *timer;
// where to create it . timer interval is in seconds & use decimals if you want split seconds.
- (void)viewDidLoad
{
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
// timer callback method
int count = 0;
- (void)onTimer:(NSNotification *)sender
{
self.state = !self.state;
if (self.state)
{
self.box1.backgroundColor = [UIColor redColor];
self.box2.backgroundColor = [UIColor blueColor];
}
else
{
self.box1.backgroundColor = [UIColor yellowColor];
self.box2.backgroundColor = [UIColor greenColor];
}
count++;
if (count == 100)
{
[self.timer invalidate];
[[[UIAlertView alloc] initWithTitle:#"timer done" message:#"timer finished" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil] show];
}
}

Making a UILabel disappear

I am working on a kids app and need to make the answer disappear after a few seconds.
What do I need to add?
- (IBAction)calculate2:(id)sender
{
float aaaa = 6;
Answer2.text = [ [NSString alloc] initWithFormat:#"%.0f ",aaaa];
}
Use NSTimer and mention the specified time for UILabel to disappear:
- (IBAction)calculate2:(id)sender
{
float aaaa = 6;
Answer2.text = [[NSString alloc] initWithFormat:#"%.0f ",aaaa];
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:NO];
}
-(void)targetMethod:(NSTimer*)timer
{
//toggle hidden parameter
[Answer2 setHidden:!Answer2.hidden];
}
-(void) hideLabel
{
[Answer2 setHidden:YES];
}
just add [self performSelector:#selector(hideLabel) withObject:nil afterDelay:4] to calculate2
For hiding it non-animated it will be:
[label setHidden:YES];
But if you want to hide it with an Animation it could be:
[UIView animatedwithduration:1.0f delay:.35f animations:{
label.alpha = 0.0;
}completion:{
[label removeFromSuperview];
//Here you can put a completed code
}];
Hope it helps!

Can't trigger function from NSTimer's selector

So here's my code :
- (IBAction)run:(id)sender {
animationPointer = 0;
self.animationArray = [[NSMutableArray alloc] init];
UIView *allViews = [[UIView alloc]
initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
for (int i = 0; i < [self.paths count]; i++) {
[allViews addSubview:[self createAnimation:[self.paths objectAtIndex:i]]];
[self.animationArray addObject:allViews];
}
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(animateTurn)
userInfo:nil repeats:YES];
}
- (void)animateTurn {
UIView *tempView = [self.animationArray objectAtIndex:animationPointer];
[self.view addSubview:tempView];
for (UIImageView *view in tempView.subviews) {
[[tempView.subviews objectAtIndex:animationPointer] startAnimating];
}
animationPointer++;
if (animationPointer >= [self.animationArray count]) {
[timer invalidate];
}
}
createAnimation returns, as you would expect, a fully animated UIImageView. The plan is to have several on each UIView and then animate them all on a UIView then the next after half a second and it repeats. There's only one on each UIView for now.
However, when calling animateTurn on the NSTimer, the animations don't show up.
If I call the method via the normal [self animateTurn] then they appear but nothing with NSTimer nor the performSelector function.
Any help with this (or even suggestions for a better method)?
EDIT:
I should mention the function is actually being triggered and the code is being run including the startAnimating (checked with BOOL/NSLog). There is simply nothing being shown.
This is Your Solution:-
This is what I used and now the selector is Working. :-)
if (![NSThread isMainThread])
{ [self performSelectorOnMainThread:#selector(selectorToRunInMainThread) withObject:nil waitUntilDone:NO];
}
-(void)selectorToRunInMainThread
{
NSString *str = #"Timer event has fired";
NSTimer *atimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateModel:) userInfo:str repeats:YES];
}
Include this line after your timer:-
[timer fire];

how to add UIButton to UIView with animation effect

I have a UIView in that, I am going to add 15 UIButtons. If I have written code like this
for(int i =0; i<15;i++)
{
[self.view addSubview:[self.buttonsArray objectAtIndex;i]];
}
But I want to subview UIButtons one after other. something like animation effect.How to achieve it.
try this:
in .h file
NSTimer *timer;
int buttonNo;
#property (nonatomic,retain) NSTimer *timer;
in .m file
#synthesize timer;
-(void) viewDidLoad {
[super viewDidLoad];
//start your timer where you write your for loop
buttonNo = 0;
timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(addbuttons) userInfo:nil repeats:YES];
}
-(void) addbuttons {
[self.view addSubview:[self.buttonsArray objectAtIndex;buttonNo]];
buttonNo++;
if (buttonNo == 15) {
[timer invalidate];
timer = nil;
}
}
I would do it like this:
const float fadein_duration = 0.3f;
const float time_between_fadein = 0.15f;
int index = 0;
for (UIButton* button in self.buttonsArray)
{
[self.view addSubview:button];
button.alpha = 0.0f;
[UIView animateWithDuration:fadein_duration delay:(index * time_between_fadein) options:0 animations:^{
button.alpha = 1.0f;
} completion:^(BOOL finished) {
// Do something maybe?
}];
index++;
}
Currently, the animation is to fade-in, but you should be able to adapt it to other types of animations.
There are 2 constants right now which you can use to control the duration of the fadein, and the time between the appearance of 2 buttons.
Make sure you set the correct frames for the buttons when you create them. Otherwise, you will just get a bunch of overlapping buttons. If you need you can set the position of the buttons dynamically when you add them to the subview.
Also, I changed the loop mode to use a fast iterator over the array, and keep a incremental index manually.
Im Unsure of what you mean. As for annimation EFFECT, you may want to implement some type of timer, otherwise they will all be added at the same time
If you want a fade in kind of animation then you can achieve something like this -
for(int i =0; i<15;i++)
{
[self.view addSubview:[self.buttonsArray objectAtIndex;i]];
[self performSelector:#selector(showBtn:) withObject:self.buttonsArray objectAtIndex;i] afterDelay:interval];
interval += 0.5f;
}
- (void) showBtn:(UIButton*) btn
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5f];
[btn setAlpha:1.0f];
[UIView commitAnimations];
}
i assume the default alpha of all buttons in array is 0.
pls try this code
for(int i =0; i<15;i++)
{
[self.view addSubview:[self.buttonsArray objectAtIndex;i]];
[self performSelector:#selector(your animated methode) withObject:nil afterDelay:0.1];
}

Resources