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];
}];
Related
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;
}];
});
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];
}
}
In my app, I do a search asynchronously. When that completes my mapViewController with pins for the locations is displayed. 2 seconds after that, I do a modal transition over to a listViewController. I set the backgroundcolor like this:
self.view.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.65];
Making it possible to see the map behind it.
The problem is this: Less then a second after the listView appears you can see the map in the background for a second before it goes away. What I can see in the background now, is the mainViewController the app starts with.
it looks like this:
Less than a second later:
Any help/explanation would be greatly appreciated.
Your view is still transparent, but once your modal controller is at the top of the stack, the view behind it is hidden.
Try using :
yourController.modalPresentationStyle = UIModalPresentationCurrentContext;
[yourController present...];
This code seems to work. I hope it will help.
-(void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
MKMapView *mapView = [MKMapView new];
[self.view addSubview:mapView];
mapView.frame = self.view.bounds;
UIGestureRecognizer *gestureRecognizer = [UITapGestureRecognizer new];
[gestureRecognizer addTarget:self action:#selector(displayTransparentVC)];
[self.view addGestureRecognizer:gestureRecognizer];
}
-(void)displayTransparentVC
{
UIViewController *vc = [TransparentViewController new];
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentViewController:vc animated:YES completion:^{
}];
}
I currently have a Parent ViewController which contains a child UIViewController (Child). The child UIViewController has a UITableView. On Child.viewDidAppear I invoke the TableView.flashScrollIndicators.
For some reason the scroll indicators are not flashing. I've noticed if I put a dispatch_after call, the second pass does a flash. I thought at first it was a size issue, but I don't think that's it. It seems to be a layout issue (I'm using Storybards, iOS 7, and no AutoLayout). Any ideas?
Here's my code for creating the Child.
- (void)viewDidLoad {
[super viewDidLoad];
// add it to the heirarchy
if ([[self childViewControllers] count] == 0) {
[self setViewControllers:[[NSMutableArray alloc] init]];
ProductSelectionViewController *destination = (ProductSelectionViewController *)[[self storyboard] instantiateViewControllerWithIdentifier:#"ProductSelectionViewController"];
[self addChildViewController:destination];
// present the child
[destination didMoveToParentViewController:self];
[[self containerView] addSubview:[destination view]];
[[self viewControllers] addObject:destination];
[destination setDelegate:self];
}
}
For me, it worked to call
[self performSelector: #selector(flashScrollIndicators) withObject: nil afterDelay: 0];
instead of
[self flashScrollIndicators];
in didMoveToWindow method of the view or in viewDidAppear of its view controller. Hope this helps people with the same issue.
I have a uitableview which loads data from internet and during this period im displaying MBProgressHUD. But the problem is the user cannot touch anything including previous page button before the table loads. Here is my codein two different classes:
//PROBLEM METHOD 1
- (void)viewDidLoad
{
[super viewDidLoad];
[tableEtkinlikler reloadData];
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Açılıyor...";
HUD.userInteractionEnabled = NO;
[self performSelector:#selector(loadEtkinliklerTitlesAndImages) withObject:nil afterDelay:0];
tableEtkinlikler.dataSource = self;
tableEtkinlikler.delegate = self;
}
I have the same problem with a button also..In it im loading data from internet..
//PROBLEM METHOD 2
- (IBAction)AktivitelerButtonClicked:(UIButton *)sender
{
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Açılıyor...";
HUD.userInteractionEnabled = NO;
[self performSelector:#selector(openAktivitelerWindow) withObject:nil afterDelay:0];
}
I believe for me that is the point of MBProgressHUD , it gives you the opportunity show a HUD while your tasks are completed, once your tasks are completed you dismiss it so user can interact with completed data.
However In some cases loading data takes so long so you might want to let the user decide to continue , choose another option or just simply go back
in your code this HUD.userInteractionEnabled = NO; should work but problem might be that showHUDAddedTo:self.view you do not use the highest view possible in the view hierarchy.
Try to use this:
- (IBAction)showSimple:(id)sender {
// The hud will dispable all input on the view (use the higest view possible in the view hierarchy)
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.userInteractionEnabled = NO;
// Regiser for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(loadEtkinliklerTitlesAndImages) onTarget:self withObject:nil animated:YES];
}