Transition between views as if one was modal - objective-c

I've got a login screen I want to hide like:
[self dismissModalViewControllerAnimated:true];
But the problem is that I need to display it like this:
-(void)viewDidAppear:(BOOL)animated {
[self presentModalViewController:loginScreen animated:false];
}
Which means I'll flash the current screen before I popup the login screen.
So what I'm looking for is a way to show the login screen instantly and transition to the main screen with the same animation as dissmissModalViewControllerAnimated:true.

You should be able to just disable the animation so its instant?
[self presentModalViewController:loginScreen animated:NO];
Do it in viewWillAppear if it still flashes briefly.

What I've done (maybe not the best solution):
- (void)viewDidLoad
{
// Initial set to hidden for avoiding a flickering UI
self.view.hidden=YES;
}
-(void)viewWillAppear:(BOOL)animated
{
[NSTimer scheduledTimerWithTimeInterval:0 block:^{
[self presentModalViewController:self.loginViewController animated:NO];
} repeats:false];
}
And just before you dispatch the ModalViewController you set self.view.hidden=NO.
See https://github.com/jivadevoe/NSTimer-Blocks for the NSTimer using blocks

Related

Obj-c show navbar after hideBarOnSwipe true

Currently the navbar will hide if the user is scrolling down. But it won't display back the navbar when user is scrolling up. how to display the navbar back when the user is scrolling up?
i am using this code to hide the navbar
self.navigationController.hidesBarsOnSwipe = YES;
The navbar will display back if the user tap twice on the top screen area, but I found it won't be so user-friendly.
Have been searching for the answers for quite a while, but can't find any
clue. What am I missing??
thanks!
I suggest you try adding scrollViewScroll as below
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGPoint scrollOffset = scrollView.contentOffset;
if (scrollOffset.y >= 40)
{
if (![self.navigationController isNavigationBarHidden])
{
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
}
else
{
if ([self.navigationController isNavigationBarHidden])
{
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
}
}
I have used one Git lib for scroll table view/ Scroll top to bottom / bottom to top. It will automatically adjust the Navigation bar.
AMScrollingNavbar
you can use like this.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[(ScrollingNavigationController *)self.navigationController followScrollView:self.tableView delay:40.0f];
}

Show UIActivityIndicator during segue navigation, after UIBarButtonItem press

I have a UIBarButtonItem in the navigation bar which switches to another screen, using a segue. This other screen takes some time to initialize, and I wanted to put a UIActivityIndicator on the UIBarButtonItem to show the tap has been registered, and the iPad is busy executing the action.
My approach was to add a UIActivityIndicator to the UIBarButtonItem after it was pressed, then call performSegueWithIdentifier:, and in the viewDidLoad method of the second view, put the initialization into a dispatch_sync() call. You can guess it does not work... why?
The IBAction on the first screen:
- (void)tappedEdit: (UIBarButtonItem *)editButton {
// put activity indicator somewhere
UIActivityIndicatorView *indicator;
indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.frame = CGRectMake (200, 5, 50, 50);
[self addSubview: indicator];
[indicator startAnimating];
// follow segue
[self performSegueWithIdentifier: SegueShowDesignMode sender: editButton];
}
The initialization on the second screen:
- (void)viewDidLoad {
[super viewDidLoad];
// put costly operations into another queue to free main queue for activity indicator
dispatch_sync (dispatch_get_global_queue (0, 0),
^{ // do initialization here
});
}
The effect of this is that the UIBarButtonItem stays tapped while the initialization is performed, then the UIActivityIndicator is visible for a quick moment, and lastly the segue animation is shown, displaying the second screen.
Any idea? Thanks a lot!
EDIT: Probably an addition to the problem is that the initialization does some UIKit stuff: When I tried to use a semaphore, the initialization dumps with a BAD ACCESS in some UITextView method. I guess this is because it runs on some 'get_global_queue' and not on the 'get_main_queue'.
EDIT AGAIN: Well, no. Using 'get_main_queue' results in a dead-lock, as announced in the Apple docs. So the question boils down to
"How can I do background UIView creation (lots of!) while still having a spinner running?"
I think one problem is that you are using dispatch_sync in your viewDidLoad method in your second screen. dispatch_sync will block your thread until the block with // do initialization here has completed. Try changing it to dispatch_async instead.
The other thing I'd consider doing if I were you is getting the UIActivityIndicator to appear as part of the second screen, not the first. That way, the second screen can also dismiss it after your view has finished the costly operations. Of course, this assumes that the view can actually display before those operations have completed.
if you are using a storyboard
ButtonBarItem.h
#import "CheckWifi.h"
interface LoadingViewController : UIViewController
-(IBAction)BarItemPressed:(id)sender;
ButtonBarItem.m:
-(IBAction)BarItemPressed:(id)sender {LoadingViewController *DIS = [self.storyboard instantiateViewControllerWithIdentifier:#"CheckWifi"];
DIS.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:DIS animated:YES];}
LoadingViewController.h:
#interface LoadingViewController : UIViewController {
IBOutlet UIActivityIndicatorView *spinner
}
-(IBAction)StopAnimating;
#end
LoadingViewController.m:
-(void)ViewDidLoad {
spinner.hidden = NO;
}
-(IBAction)StopAnimating {
spinner.hidden = YES;
}
If you're using an XIB File, then I cannot help you really, Sorry.

How show activity-indicator when press button for upload next view or webview?

when i click on button which title is click here to enlarge then i want show activity indicator on the first view and remove when load this view.
but i go back then it show activity indicator which is shown in this view.
in first vie .m file i have use this code for action.
-(IBAction)btnSelected:(id)sender{
UIButton *button = (UIButton *)sender;
int whichButton = button.tag;
NSLog(#"Current TAG: %i", whichButton);
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[spinner setCenter:CGPointMake(160,124)];
[self.view addSubview:spinner];
[spinner startAnimating];
if(whichButton==1)
{
[spinner stopAnimating];
first=[[FirstImage alloc]init];
[self.navigationController pushViewController:first animated:YES];
[spinner hidesWhenStopped ];
}}
in above code i have button action in which i call next view. Now i want show/display activity indicator when view upload. In next view i have a image view in which a image i upload i have declare an activity indicator which also not working. How do that?
Toro's suggestion offers a great explanation and solution, but I just wanted to offer up another way of achieving this, as this is how I do it.
As Toro said,
- (void) someFunction
{
[activityIndicator startAnimation];
// do computations ....
[activityIndicator stopAnimation];
}
The above code will not work because you do not give the UI time to update when you include the activityIndicator in your currently running function. So what I and many others do is break it up into a separate thread like so:
- (void) yourMainFunction {
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[NSThread detachNewThreadSelector:#selector(threadStartAnimating) toTarget:self withObject:nil];
//Your computations
[activityIndicator stopAnimating];
}
- (void) threadStartAnimating {
[activityIndicator startAnimating];
}
Good luck!
-Karoly
[self.navigationController pushViewController:first animated:YES];
Generally, when you push a view controller into navigation controller, it will invoke the -(void)viewWillAppear: and -(void)viewDidAppear: methods. You can add activity indicator view inside the viewWillAppear: and call startAnimation of indicator view. You CANNOT invoke startAnimation and stopAnimation at the same time. For example,
- (void)viewWillAppear:(BOOL)animated
{
[aIndicatorView startAnimation];
// do somethings ....
[aIndicatorView stopAnimation];
}
Because the startAnimation and stopAnimation are under the same time, then no animation will show.
But if you invoke startAnimation in -(void)viewWillAppear: and invoke stopAnimation in another message, like followings.
- (void)viewWillAppear:(BOOL)animated
{
[aIndicatorView startAnimation];
// do somethings...
}
- (void)viewDidAppear:(BOOL)animated
{
[aIndicatorView stopAnimation];
}
Because viewWillAppear: and viewDidAppear: are invoked with different event time, the activity indicator view will work well.
Or, you can do something like followings:
- (void)viewWillAppear:(BOOL)animated
{
[aIndicatorView startAnimation];
// Let run loop has chances to animations, others events in run loop queue, and ... etc.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
// do somethings ....
[aIndicatorView stopAnimation];
}
The above example is a bad example, because it invokes two or more animations in the -runUntilDate:. But it will let the activity indicator view work.
Create a webview. Add a activity indicator to the webview. If you are loading a image via url into the webview then implement the webview delegate methods. Once the url is loaded then stopanimating the activity indicator.
Let me know which step you are not able to implement.

objective-c modalViewController too quick

I am having an issue dismissing a modal view controller on a certain edge case. I display the modal view when I am retrieving a PDF to display in a UIWebView. When the file I am retrieving is very small the modal view will try to dismiss too soon. I present the modal view in the view controller that contains the UIWebView. I dismiss it in the UIWebView's didFinishLoad delegate method.
I am fine with not animating the initial presentation of the modal view... but is that any more safe than what I was doing? does this still have potential to fail, and if so how would you change it? I have been looking through the docs and nothing I have read so far adresses this situation.
//
// This will download the file if not # specific path, otherwise use local file.
// _myFileManager is a helper class and _myFileRecord is the backing data model
//
-(id)initWithNib... fileRecord:(MYFileRecord *)_myFileRecord
{
[_myFileManager cacheFileAsync:_myFileRecord delegate:self];
}
- (void)viewDidLoad
{
// doesn't seem to work, NO for animated does seem to work
[self.navigationController presentModalViewController:_splashController
animated:YES];
_splashController.messageLabel.text = #"Retrieving File...";
}
- (void)recordSaved:(MyFileRecord *)myFileRecord fileName:(NSString *)fileName
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:fileName]];
[_webView loadRequest:request];
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
_splashController.messageLabel.text = #"Opening File...";
}
//
// This fails when a small file is already cached to disk and the time
// for the webView to finishLoad is faster than the splashView can present itself
//
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[self.navigationController dismissModalViewControllerAnimated:YES];
}
Try implementing the viewDidAppear in your SplashController, to catch when the view has finished animating, and set a flag. Then you can control if the SplashController's view has finished loading using this flag, and wait for it if it is not finished yet?
E.g.
-(void)viewDidAppear {
if (shouldDismiss) {
[self dismissViewControllerAnimated:YES];
}
readyToDismiss = YES;
}
And in your main VC:
-(void)webViewDidFinishLoading:(UIWebView*)webViewv
{
if (_splashController.readyToDismiss) {
[_splashController dismissViewControllerAnimated:YES];
} else {
_splashController.shouldDismiss = YES; // will dismiss in viewDidAppear
}
}
You can try testing to see if the splashView has finished and use performSelector:afterDelay: to check back later.
My idea is to create a method like this
-(void)dismissWhenReady {
if ( splashView is finished) {
[self.navigationController dismissModalViewControllerAnimated:YES];
} else
[self performSelector:#selector(dismissWhenReady) afterDelay:1.0];
}
}
viewDidLoad fires too early (before it is displayed), you will want to use -(void)viewDidAppear:(BOOL)animated to present your modal view instead along with a flag to know if it is the first load. If it still does not display long enough add a delay for the desired amount of time.

Don't allow user interaction when activity indicator view is visible

I have a view which contains two views. One of those views contains two buttons and some text labels. The other one, with alpha set to 0.25, has an UIActivityIndicatorView to tell the user that the app is working and he must wait until it finishes. If the user touch a button while the UIActivityIndicatorView is spinning, when the UIActivityIndicatorView stops, the app remember the user action and responds to it. How can I discard the user interaction that occur while the UIActivityIndicatorView is spinning?
Thanks for reading.
P.D.: Like is commented in this thread, I prefer do not to use any modal solution.
EDITED:
I am currently using this code and it does not work right.
- (void)viewDidAppear:(BOOL)animated {
// The view appears with an UIActivityIndicatorView spinning.
[self showResults]; // The method that takes a long time to finish.
[self.activityIndicator stopAnimating];
// When the showResults method ends, the view shows the buttons to the user.
[self.activityIndicatorView setHidden:YES];
[self.menuButton setEnabled:YES];
[self.menuButton setUserInteractionEnabled:YES];
[self.playButton setEnabled:YES];
[self.playButton setUserInteractionEnabled:YES];
[self.view setUserInteractionEnabled:YES];
[self.interactionView setUserInteractionEnabled:YES];
}
I found these methods very useful:
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
In Swift 3.0
To Disable interaction :-
UIApplication.shared.beginIgnoringInteractionEvents()
To restore interaction :-
UIApplication.shared.endIgnoringInteractionEvents()
[_button setUserInteractionEnabled:NO];
That should disable it, just set YES for when you want to user to tap it.
BOOL i_am_ready_to_submit = NO;
-(void)action_finished{
[self.activityIndicator stopAnimating];
i_am_ready_to_submit = YES;
}
-(IBAction)submit_button{
if(i_am_ready_to_submit){
[self submit];
}
}
just add
[self.view setUserInteractionEnabled:NO];
before the
[self.activityIndicator startAnimating];
and reenable it after
[self.activityIndicator stopAnimating];
[self.view setUserInteractionEnabled:YES];
To disable touch event in a view,
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
To enable touch event in a view
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
For swift 5 you can use:
self.view.isUserInteractionEnabled = false
and to enable all again:
self.view.isUserInteractionEnabled = true
If you have some lag problem, like freeze for about a couple of seconds just put this in:
DispatchQueue.main.async{}
You could disable/enable the UIButtons based on the UIActivityIndicatorView being shown or not. Or, if you just want to "discard the user interaction" while the spinner is shown, in the button handler method:
- (void)buttonTapped:(id)sender {
if ([spinner superview] != nil && [spinner isAnimating]) {
return;
}
// ... the rest of your code
}
This example assumes that when you hide the UIActivityIndicatorView you call one of:
[spinner removeFromSuperview];
or
[spinner stopAnimating];
Use SVProgressHUD WrapperClass It have so many options to show ActivityIndicator
For Source Code Click Here !
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeBlack];
use above statement to disable background touches
[SVProgressHUD dismiss]
To enable background touches.
#IBAction func yourButtonPressed(sender: UIButton) {
if self.activityIndicator.isAnimating() {
//remember the action user asked of you using the sender
} else {
//do your stuff
return
}
yourButtonPressed(yourButton)
}
or you code use
self.activityIndicator.animationDidStop to determine when to run your stuff
A quick solution: add a transparent or pseudo transparent view that cover the whole screen. Add your activity indicator on top of this view. When the wait period finishes, remove both views. Get some inspiration.
A better solution, because you can't hide the whole screen in all situations, is to manage the state of the app (ignore actions when the app is 'busy') and disable/enable the appropriate buttons and other controls depending on each app state.
Though answer is replied in earlier response, just like to add for information purpose "[self.activityIndicatorView setHidden:YES];" no need to call this method explicitly, because startAnimating/stopAnimating already take care of this. I'm assuming you are using default value of "hidesWhenStopped" property.