I have an app in which I present a menu modally. Then, when the user selects a menu option, the menu dismisses itself and upon completion, presents another view modally according to what button the user selected.
This method worked flawlessly before, but since iOS 13 it behaves weirdly. Basically, it would not present the second view until the user touches the screen, and I cannot for the life of me find out why it behaves this way.
Here is my code (I added logs where the flow stops):
- (void) dismissPopUpMenu:(UIButton *)sender {
[self dismissViewControllerAnimated:NO completion:^{
if ( (sender.tag == 13 ) {
[self openPopUpWindow:sender];
}
}];
}
- (void) openPopUpWindow:(UIButton *)sender {
interactionDisabledLayer.alpha = 1.0f;
PopUpViewController *popUpController = [[PopUpViewController alloc] initWithPopUp:sender.tag];
popUpController.delegate = self;
NSLog(#"We get to here without problems.");
[self presentViewController:popUpController animated:NO completion:^{
NSLog(#"It only enters here if we touch the screen.");
self->interactionDisabledLayer.alpha = 0.0f;
}];
}
Update: Delaying the code with a timer help, but I consider it a dirty hack and would like to find a better solution.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self presentViewController:popUpController animated:NO completion:^{
self->interactionDisabledLayer.alpha = 0.0f;
}];
});
Delaying the code that presents the second UIViewController with a timer seem to help, but it is a rather dirty hack, and I would like to find a better workaround. If anyone has a proper solution, please post it as an answer and I will accept it as best.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1f * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self presentViewController:popUpController animated:NO completion:^{
self->interactionDisabledLayer.alpha = 0.0f;
}];
});
Related
I am new to this development and i was working with splash screen but I don't know how to move from one view controller to another with some certain time...
[self performSegueWithIdentifier:#"nameOfYourSegue" sender:self];
I don't know how to add time any help please.....
You can try
// delay 1 second
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"segueID" sender:nil];
});
OR
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
UIViewController*vc = [self.storyboard instantiateViewControllerWithIdentifier:#"VCID"];
[self presentViewController:vc animated:YES completion:nil];
});
Timer Documentation
Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { (timer) in
self.performSegue(withIdentifier: "nameOfYourSegue", sender: nil)
}
Add this code in viewDidLoad
[self performSelector:#selector(LoadMainPage:) withObject:nil afterDelay:3.0];
and then,
-(void)LoadLoginPage
{
LoginViewController *loginView=[self.storyboard instantiateViewControllerWithIdentifier:#"loginViewController"];
[self.navigationController pushViewController:loginView animated:YES];
}
If you want to display a splash screen before your initial view controller, you should read the Apple's Human Interface Guidelines.
Also, here is a simple tutorial to help you create your own splash screen.
Happy coding ;)
So I have a VC that i call on when a button is clicked (It's a MFMailComposeViewController) and when the message is sent, It dismisses the ViewController and i want it to present a different one once it's done sending. But instead of doing this it crashes after sending the email every time. i know I'm doing something wrong but i'm not sure what.
Here's my code that dismisses and presents the new one.
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[self dismissViewControllerAnimated:true completion:nil];
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];}
I was thinking that the problem was that I used self but I'm not sure what I use in place of that.
It is crashing because you are asking for both dismiss and present animations to begin simultaneously. So you can fix this in 2 ways:
Wait for the first animation to complete before starting the next one. This will animate the dismissal of the current VC and animate the presentation of the new one. Of course the user would have to wait for both animations to complete before they can continue interacting with the app. To do this, present _emailConfirmationPage in the completion block of dismissing the current VC like this:
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];
}];
Animate either dismiss OR present, but not both. This might be better because the user would have to wait only for 1 animation and that 1 animation will most probably be sufficient to ensure a fluid user experience.
[self dismissViewControllerAnimated:NO completion:nil];
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];
Try put your [self presentViewController:_emailConfirmationPage animated:YES completion:nil];} into completion: block in the [self dismissViewControllerAnimated:true completion:nil]; , it will execute present vc after it has complete dismiss the other vc
Like this:
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];
}];
Your code did not work because its still animating dismissViewController and there can't be 2 animation at the same time
Im experimenting with iOS 8 Objective-c project to perform a segue to a view controller right when viewDidLoad is presented.
Then this recent new presented view should be dismissed by a 30 seconds countdown to return to the initial view controller.
What would be like the code to dismiss the view with a countdown ? A countdown interface would not be necessary, just the background code.
My initial view controller is called ConfViewController and will perform a segue right on viewDidLoad to BubbleViewController, this should be displayed for 30s then be dismissed to the first one again ConfViewController.
#implementation ConfViewController
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)viewDidLoad
{
[super viewDidLoad];
The BubbleViewController
#interface BubbleViewController ()
#end
#implementation MessagesViewController {
UIVisualEffectView *blurEffectView;
}
- (void)viewDidLoad
{
self.dataSource = self;
self.delegate = self;
[super viewDidLoad];
Thnx and cheers!
Putting this Code into your bubble view Controllers viewDidLoad or similar and it will dismiss itself after 30 seconds:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
});
A quick way to execute any code after some time is to use this snippet:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//code to be executed after a specified delay
});
Where you should place this code is up to you either in the presenters or in the presented controllers viewDidLoad.
How you should dismiss it depends on the way that you presented it.
Add below line of code in BubbleViewController's viewDidLoad method
//countdown can be according to requirement
[self performSelector:#selector(dissmissView) withObject:nil afterDelay:30];
Now description of dissmissView method and add these method also.
-(void)dissmissView
{
if (self.navigationController.presentingViewController)
{
//presented As ModaL
[self dismissViewControllerAnimated:YES completion:nil];
}
else if(self.navigationController)
{
//Pushed in navigation controller stack
[self.navigationController popViewControllerAnimated:YES];
}
else //added as subView
{
[self.view removeFromSuperview];
}
}
I use the following code to get the tap on image view and navigate to another view controller:
-(void)tapDetected:(UIGestureRecognizer *)recognizer{
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
NSLog(#"single Tap on imageview");
ImageTapGesture *sender = (ImageTapGesture *)recognizer;
UIStoryboard * storyboard = self.storyboard;
tele * detail = [storyboard instantiateViewControllerWithIdentifier: # "teledramaView"];
detail.imagelink = sender.imageLink;
detail.description = sender.description;
detail.id = sender.id;
[self.navigationController pushViewController: detail animated: YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
So far the code is working fine but the issue is the progress bar is not displaying properly.
It shows at the final moment before going to the other controller but the log comes before the progress bar: NSLog(#"single Tap on imageview");
Can anybody tell me why the progress bar is not working as it's meant to work? Thank you.
Well the answer is simple, since you are showing and hiding the the progressHUD in the same thread it will get removed as soon as it is added. Since you methods is executed on the main thread, which is also handeling any UI changes, your progressHUD will only be added after you method end. But since you remove the progressHUD on the last line it will also be removed directly. Even worse is that any code between adding and removing the HUD is blocking any UI changes.
You need to delay the loading a bit, to give the system time to present the progressHUD:
-(void)tapDetected:(UIGestureRecognizer *)recognizer{
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
NSLog(#"single Tap on imageview");
[self performSelector:#selector(navigateToDetailView:) withObject:recognizer afterDelay:0.0];
}
-(void) navigateToDetailView:(UIGestureRecognizer*)recognizer{
ImageTapGesture *sender = (ImageTapGesture *)recognizer;
UIStoryboard * storyboard = self.storyboard;
tele * detail = [storyboard instantiateViewControllerWithIdentifier: # "teledramaView"];
detail.imagelink = sender.imageLink;
detail.description = sender.description;
detail.id = sender.id;
[self.navigationController pushViewController: detail animated: YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
I've implemented the MFMailComposeViewController and encountered a problem.
In my superview, I implemented a button, when pressed, brings up in-app emailing.
Everything works fine, until when the user sends or closes the mail form.
It seems like I need to wait for the animation (dismiss the email form) to end first, before [self.view removeFromSuperview]; can be run. If this line is run before the animation ends nothing happens and the view is not removed, thus I can't tap anything on the screen as the current UIView is not removed.
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
[self dismissModalViewControllerAnimated:YES];
// Removes the view so that delegate is passed back to the superview
[self.view removeFromSuperview];
}
My superview action which calls the class:
- (IBAction)buttonPressedSendMail {
MailComposerViewController *mailComposer = [[MailComposerViewController alloc] init];
[self addSubview:mailComposer.view];
[mailComposer sendMail:nil];
}
[[self view] setHidden:YES]; to hide the UIView works, but I don't think this is the right way of doing it.
Present your view controller with presentModalViewController:animated:
MailComposerViewController *mailComposer = [[MailComposerViewController alloc] init];
[self presentModalViewController:mailComposer animated:YES];
Then dismiss the modal view like you do
[self dismissModalViewControllerAnimated:YES];
No need to add or remove the view controller's view.
You can use the dispatch_after block. You can adjust the delayInSeconds to after the mail composer dismissed.
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.view removeFromSuperview];
});
or you can try this:
[self dismissViewControllerAnimated:YES completion:^{
[self.view removeFromSuperview];
}];