iOS Add loading text with animating period character - ios

Not sure how to describe this, but what would be the easiest way to have a UILabel display Loading, then Loading., then Loading.., then Loading..., and then repeat?
I have been looking into timers but it all seems a bit excessive. Anyone know of a cool and quick trick to pull something like that off? Thanks!

Not sure about your specific use case (more info please or code?) but have you tried animation blocks? Something like:
- (void)animate
{
__block UIView * blockSelf = self;
[UIView animateWithDuration:0.1f animations:^{
if ([blockSelf.label.text isEqualToString:#"Loading..."]) {
blockSelf.label.text = #"Loading";
} else {
blockSelf.label.text = [NSString stringWithFormat:#"%#.", blockSelf.label.text];
}
} completion:^(BOOL finished) {
if (blockSelf.processIsFinished) {
[self moveOn];
} else {
[blockSelf animate];
}
}];
}
Alternatively, something like MBProgressHUD might be useful depending on what type of process is "loading".

Related

Pausing an animation in iOS (objective C)

I am having some trouble handling animations in some objective C code.
First, here is the relevant code:
BOOL pauseFlag; // Instance variable.
CGFloat animationDuration,pauseDuration; // Instance variables.
......
pauseFlag = NO;
animationDuration = 1.0;
pauseDuration = 1.0;
- (void)animationFunction
{
[UIView animateWithDuration:animationDuration
delay:pauseFlag?pauseDuration:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
......
}
completion:^(BOOL finished){
......
pauseFlag = Some_New_Value;
[self animationFunction];
}];
}
Then here is the problem:
The delay supposed to take place when pauseFlag is YES is not happening.
Of course, before writing this post I have tried various solutions which came up to my mind, like changing the options, and I also checked that when entering animationFunction pauseFlag had the value YES. But in all cases the delay was ignored.
What did I do wrong? I need to insert a pause in my animation and thought this was the simplest way to do it.
Anyone has an idea?
Just for information, beside this issue. This animation code is working fine.
Try to animate your view with UIViewPropertyAnimator:
UIViewPropertyAnimator* animator = [UIViewPropertyAnimator runningPropertyAnimatorWithDuration:animationDuration
delay:pauseFlag?pauseDuration:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
......
}
completion:^(UIViewAnimatingPosition finalPosition) {
......
pauseFlag = Some_New_Value;
[self animationFunction];
}];
If you want to pause the animation call pauseAnimation:
[animator pauseAnimation];
To resume it call startAnimation:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[animator startAnimation];
});
All code in this post was tested in Xcode 10.2.1.

Wait for view controllers animations to finish before taking screenshot

I have a method that programmatically will take a screenshot [self makeScreenshot]. But sometimes, when a rotation happens for example, the result can be very ugly with black parts in it. So I’m trying to make a method with a completion that will wait for the view controllers animations to finish, so that the screenshots can be made safely and always look nice. The call would maybe look something like this:
[self methodWithCompletionWhenAnimationsIsDone:^(BOOL finished) {
// now it's safe to make the screenshot.
UIImage *myScreenshot = [self makeScreenshot];
}];
But I cant figure out how the code for such a method would look like. Any suggestions?
I’ve tried to place screenshot code the - (void)viewDidLayoutSubviews-method and it didn't work. And I don't want to use any rotation callback methods either, because the problem occurs at other occasions as well.
I don't know which method for animation you are using, but I will give you an example of the default UIView animation with completion:
[UIView animateWithDuration:1.0 animations:^{
/* Some animation here */
} completion:^(BOOL finished) {
UIImage *myScreenshot = [self makeScreenshot];
}];
Maybe you try to perform it in background thread
[self methodWithCompletionWhenAnimationsIsDone:^(BOOL finished) {
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *myScreenshot = [self makeScreenshot];
});
}];

I can't observe the progress value of UIProgressView with KVO when I use the -setProgress:animated:

I have set
[self.progressView addObserver:self forKeyPath:#"progress" options:NSKeyValueObservingOptionNew context:nil];
if I use self.progressView.progress = 0.1; to change progress, it's ok.
but if I use [self.progressView setProgress:0.1 animated:YES];, the method -observeValueForKeyPath:ofObject:change:context: can't be trigged.
what can I do now? now I can't change [self.progressView setProgress:0.1 animated:YES]; because this code in another lib.
You're probably out of luck here. You can't control the implementation of UIProgressView. File a bug with Apple if you like, but I wouldn't hold my breath, because...
Conceptually, w/r/t the MVC pattern used in iOS and OS X, this is not something you should be doing in the first place. Views should be the "observers", not the "observees". You should find another way to achieve this.
Yes, you could probably hack this into submission by swizzling the implementation of -setProgress:animated: to call -willChangeValueForKey: and -didChangeValueForKey: before and after calling the original implementation, but do yourself a favor and find another way.
I'm run into this issue too.
I'm using the following code to solve this.
CGFloat progress = 0.3;//this is just a example.
if (progress < self.progressView.progress) {
self.progressView.progress = progress;
}
else
{
[UIView animateWithDuration:0.3 animations:^{
[self.progressView setProgress:progress animated:YES];
}completion:^(BOOL finished) {
//avoid key-value observer invalid
self.progressView.progress = progress;
}];
}
This is working for me now.
Hope it could help someone.

iOS prevent rotations until after animation completes

I'm creating an iOS app and in one method I have a for loop that does a series of animations that last around 2 seconds.
My problem is that if the user rotates the device while the animations are still in progress, it messes up the formatting for the new orientation (everything works just the way it should if the rotation happens after the animation is complete).
So I was wondering if there is a way to delay rotations
You could have a BOOL instance variable that you update based on whether your animation is complete or not. Then override the shouldAutorotate method and return that BOOL. Might look something like this:
#implementation YourViewController {
BOOL _shouldAutoRotate;
}
-(BOOL)shouldAutorotate {
[super shouldAutorotate];
return _shouldAutoRotate;
}
-(void)yourAnimationMethod {
_shouldAutoRotate = NO;
[UIView animateWithDuration:2.0f animations:^{
//your animations
} completion:^(BOOL finished) {
if(finished) {
_shouldAutoRotate = YES;
}
}];
}

iPhone NSTimer running single method

I'm new to programming for the iPhone and i'm having a problem with this function
-(IBAction)changeX {
[self timere:field1];
[self timere:field2];
}
this is a button to move an uitextfield object across the screen. The problem is i want to run this method on the first field , complete it then go on to the second. The timere function uses NSTimer to continously move the object until it reaches a certain point at which it terminates. I have two other functions shown below. The actual program im making is much longer but the objective is the same and that code is too long. The problem is running the first function then the second. Thank you for the help.
-(void)timere:(UITextField*)f1 {
UITextField*fielder1=f1;
NSInvocation*timerInvocation = [NSInvocation invocationWithMethodSignature:
[self methodSignatureForSelector:#selector(moveP:)]];
[timerInvocation setSelector:#selector(moveP:)];
[timerInvocation setTarget:self];
[timerInvocation setArgument:&fielder1 atIndex:2];
timer1 = [NSTimer scheduledTimerWithTimeInterval:0.03 invocation:timerInvocation repeats:YES];
}
-(void) moveP:(UITextField*)f1 {
UITextField*fielder1=f1;
fielder1.center = CGPointMake(fielder1.center.x+4, fielder1.center.y);
if (fielder1.center.x>237) {
[timer1 invalidate];
}
}
The standardized method of animating UIView objects is by using methods such as the following.
+ animateWithDuration:delay:options:animations:completion:
The syntax for these methods might be a bit daunting if you are unfamiliar with Objective-C Blocks. Here's a usage example which moves the first field, then the second field afterwards.
[UIView animateWithDuration:0.3 animations:^{
[field1 setCenter:CGPointMake(FINAL_CENTER_X1, FINAL_CENTER_Y1)];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
[field2 setCenter:CGPointMake(FINAL_CENTER_X2, FINAL_CENTER_Y2)];
}];
}];
EDIT: For a general fix on your specific problem, I would make the following modifications:
-(IBAction)changeX {
[self timere:field1];
}
-(void) moveP:(UITextField*)f1 {
UITextField*fielder1=f1;
fielder1.center = CGPointMake(fielder1.center.x+4, fielder1.center.y);
if (fielder1.center.x>237) {
[timer1 invalidate];
// I'm really not sure about the scope of field1 and field2, but
// you can figure out the encapsulation issues
if (f1 == field1) {
[self timere:field2];
}
}
}
A more generic and indeed low-level solution would be to have a state variable. Perhaps you would have some sort of NSArray *fields of UITextField * and int state = 0. Where I added the conditional in moveP above, you would state++ and call [self timere:[fields objectAtIndex:state]] if state < [fields count]. You timer invalidation code is correct, anyway.

Resources