objective-c modalViewController too quick - objective-c

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.

Related

shouldPerformSegueWithIdentifier always executed first

I have a function called "openButtonPressed" which gets executed when a button on the ui gets pressed. Now I would like to show a loading view at first and then execute the segue. For some reason, the segue always gets called first.
Does somebody have a clue?
Thank you!
- (void)openButtonPressed:(id)sender
{
NSDebug(#"Open Button");
[self showLoadingView];
static NSString* segueToContinue = kSegueToContinue;
if ([self shouldPerformSegueWithIdentifier:segueToContinue sender:self]) {
[self performSegueWithIdentifier:segueToContinue sender:self];
}
}
- (void)showLoadingView
{
if(!self.loadingView) {
self.loadingView = [[LoadingView alloc] init];
[self.view addSubview:self.loadingView];
}
[self.loadingView show];
}
It looks like LoadingView is being initialized without a size.
It also may be executing the segue very quickly and you don't have any time to see the loading view.
You can set a breakpoint at the performSegue call and use the view debugger in Xcode to confirm if the view was loaded and what size the view was initialized to.
Maybe the view is not in the right place or in the right size. But even if you fix that you need to add a delay so the user can see the loading... I replicated your scenario here and after giving the view a initWithFrame it appears, but cannot be seen because is too fast...
If you want to show the loading for a period of time before the performSegue, you could use the performSelector:withObject:afterDelay.
If you need to wait an action from the loadingView, create a completionHandler in the loadingView initializer...
The hints below were very helpful to me and totally a better solution but I ended up using [CATransaction flush]; to force the refresh.
- (void)openButtonPressed:(id)sender
{
NSDebug(#"Open Button");
[self showLoadingView];
[CATransaction flush];
static NSString* segueToContinue = kSegueToContinue;
if ([self shouldPerformSegueWithIdentifier:segueToContinue sender:self]) {
[self performSegueWithIdentifier:segueToContinue sender:self];
}
}

viewDidAppear not firing

I know this has been asked but I've tried all the solutions to no avail.
One of the tabs of my application is Logs
When it is selected a log viewcontroller with a list of logs is displayed
I have a "+" on the top menu bar to add new logs
When I select it I push the add log view controller on the stack
I add a new log - I then take the back button
when I return to the table the list is not updated
I wanted to reload the data in the table upon returning to the first view controller but
neither viewDidLoad, viewDidAppear or viewWillAppear are fired upon returning to the table
Since the first screen is oblivious
I added these to the second screen in an attempt to find any action occurring when the back button
-(void)viewWillDisappear{
NSLog(#" whered it go");
}
-(void)viewDidDisAppear
{
NSLog(#" disapeeeeeearing");
}
- (void)backAction {
NSLog(#" WHAT ABOTU THIS ");
[self.navigationController popViewControllerAnimated:YES];
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
[viewController viewWillAppear:animated];
NSLog(#"is ANY of this Happening");
}
NONE of these fire when the add screen disappears no NSLog printed
From what I've read the lack of response on the first screen is because the log table never really left the stack .
This didn't happen when I did this on another tab (different part of the app)
Whats different about this tab is the add log view controller has 5 subviews and can call modals (though it fails even when I don't call them) - I'm guessing it has something to do with that.
The call from the logs to the add logs
AddViewController *controller =[[AddViewController alloc] initWithNibName:#"AddViewController" bundle:nil];
controller.passString= newText;
[self.navigationController pushViewController:controller animated:YES];
Don't have any code for the return it just clicking on the back button at the top of the screen.
sorry for any misspellings
That's because you aren't using the correct methods.
-(void)viewWillDisappear{
NSLog(#" whered it go");
}
and
-(void)viewDidDisAppear
{
NSLog(#" disapeeeeeearing");
}
Aren't declared anywhere. I believe what you meant to write was:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
NSLog(#"About to disappear");
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
NSLog(#"Did disappear");
}
Methods are case sensitive, and you can't just leave out arguments and expect it to work.

Why isn't activity indicator working for web view?

I am trying to load a web view (called widget) and set an indicator (called indicator) to show that the view is loading (the web page is no larger than www.google.com). The widget loads fine (I haven't included that code) but this snippet, which should send startAnimating to a UIActivityIndicatorView is not working (no animation or even appearance). Everything is connected in storyboard and "not loading" is always logged. This makes me think there is something wrong with my use of UIWebView's loading property.
sleep(2);
[super viewDidLoad];
[indicator setHidesWhenStopped:YES];
if (widget.loading == YES) {
NSLog(#"loading");
[indicator startAnimating];
} else {
NSLog(#"not loading");
[indicator stopAnimating];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
Any thoughts?
Thanks
Try putting your start and stop animating in the delegate methods for the webview:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[indicator stopAnimating];
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
[indicator startAnimating];
}
Make sure you set the webview's delegate and are conforming to UIWebViewDelegate in your interface.

viewWillAppear not called in BCTabBarController

I've large project where customer want's to customize tabbar. I've choose BCTabBarController to replace UITabbarController. After few fixes it works fine but after testing I found one bug:
ViewWillAppear, ViewDidAppear, ViewWillDisappear ViewDidDisappear methods not called in selectded view controller and not called into BCTabBarController.
This problem appears after BCTabBarController show modal controller from instance of BCTabBarController class.
I've posted issue to github repo of briancolins, but still have no answer.
Here some code where I calling present modal view controller:
- (void) presentProperlyModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
{
if ([[self controllerToPresentModalFrom] respondsToSelector:#selector(presentViewController:animated:completion:)]) // For iOS 5
{
[[self controllerToPresentModalFrom] presentViewController:modalViewController animated:animated completion:^(){}];
}
else
{
[[self controllerToPresentModalFrom] presentModalViewController:modalViewController animated:animated];
}
}
-(void) dismissProperlyModalViewControllerAnimated:(BOOL)animated
{
if ([self respondsToSelector:#selector(dismissViewControllerAnimated:completion:)]) {
[self dismissViewControllerAnimated:animated completion:^(){}];
}
else
{
[self dismissModalViewControllerAnimated:YES];
}
}
UPDATE: this issue not reproduced in iOS5 but present at iOS 4.3
As you indicated. iOS 5 forwards the messages, where previous versions do not. Here's how I handle a similar situation:
- (BOOL)needsMessageForwarding:(UIViewController *)vc {
if ( [vc isKindOfClass:[UINavigationController class]] == NO)
return YES;
NSString *ver = [UIDevice currentDevice].systemVersion;
if ( [ver characterAtIndex:0 < '5'] )
return YES;
return NO;
}
- (void) viewWillAppear:(BOOL)animated {
...
if ( [self needsMessageForwarding:modalViewController] )
[modalViewController viewWillAppear:animated];
...
}
// repeat pattern in the other viewWill... viewDid... functions.
In my situation I have a list of view controllers that could be visible, so I manage which view controller is visible and forward the message to it.

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.