The ABPeoplePickerNavigationController class does not support subclassing - ios

In using ABPeoplePickerNavigationController, didSelecPerson delegate will get called when user selects a person and then the ABPeoplePickerNavigationController dismisses itself. I need to present another view controller when receiving didSelectPerson call. The issue is ABPeoplePickerNavigationController's animation dismissing at this stage is not complete. So, the presentation of the next view controller will generate an error.
If I subclass ABPeoplePickerNavigationController and override:
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[super dismissViewControllerAnimated:flag completion:completion];
[self.myDelegate didDismissPickerCompletion];
}
That will solve the problem. But the Apple doc did state "The ABPeoplePickerNavigationController class does not support subclassing". Does that mean they will reject any attempt to subclass such stated class?
note: I do not wish to use any artificial time delay as a solution.

and then the ABPeoplePickerNavigationController dismisses itself
There's your problem. It is dismissing itself only because you did not dismiss it. Dismiss it, explicitly, yourself! Call dismissViewControllerAnimated:completion: right there in your didSelectPerson: implemention.
Now you have a completion handler, and so you can run code when the dismissal has finished.

Related

iOS presentViewController doesn't invoke viewDidLoad

I'm implementing my own 'back' button. Where onClick, the following code is executed in the ViewController (VC) being dismissed:
Dismiss current VC (VC#1)
Pop current VC (VC#1) off my custom navigationStack
Get the last VC (VC#2) from the navigationStack, and present it using
presentViewController
What happens is the back works visually works - i.e. current VC disappears, previous VC appears. However, the viewDidLoad method is not called. So the screen isn't updated with data updates from viewDidLoad.
[self dismissCurrentViewController:self completion:^{
[TWStatus dismiss];
FHBaseViewController *vcToDisplay = [[FHDataManager sharedInstance] popNavigationStack];
[vcToDisplay.homeVC presentViewController:vcToDisplay animated:NO completion: ^{ }];
}];
Questions:
I was under the impression that viewDidLoad always gets called when presentViuewController is used??
I 'build' the screen using a method called ONLY from viewDidLoad in VC#2. How is iOS displaying the screen without coming into viewDidLoad?
btw, I'm not using storyboards. Any help is appreciated!
My guess is that viewWillAppear is being called but viewDidLoad is not, at least not when you expect it is. viewDidLoad should be called once, but depending on how you're managing the view controllers, viewDidLoad may not be triggered every time your view appears (which happens after loading).
The completion handler is called after the viewDidAppear: method is called on the presented view controller. from presentViewController doc
so put this in your code with a breakpoint on the call to super and verify it is getting called when this transition occurs.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
edit: since you verified that viewWillAppear is getting called, then I would say that it's coming down to how you are managing the view controller life cycle. Even with a standard UINavigationController, viewDidLoad is not called when a view is shown as a result of popping items on the navigation stack. I would move your logic to viewWillAppear if you are dead set on not using UINavigationController
When I make a back button pragmatically I use:
[self.navigationController popViewControllerAnimated:YES];
This will invoke the viewDidLoad method. Use that instead of your current code.

Is it Possible to Dismiss MFMailComposeViewController Without User Interaction?

I am able to successfully dismiss my MFMailComposeViewController in the didFinishWithResult delegate method. However, I have a scenario where I would like to dismiss the composer without user interaction, like selecting cancel or sending the mail.
I have looked in apple docs and was unable to find anything entirely useful. I have tried calling dismissViewControllerAnimated but that only seems to be working when I am inside the didFinishWithResult delegate method. Is there anyway to force that delegate method or dismiss the composer alternatively?
Assuming you are presenting your mail controller from a UIViewController, you may dismiss it programmatically by calling the UIViewController method:
dismissViewControllerAnimated:completion:
See this apple reference: dismissViewControllerAnimated:completion:
You did mention:
I have tried calling dismissViewControllerAnimated but that only seems
to be working when I am inside the didFinishWithResult delegate method
What you are experiencing may be indicative of a different problem as I was able to successfully do this outside of the mailComposeController:didFinishWithResult:error: delegate method.
Example:
-(void)showMail
{
MFMailComposeViewController *mailController = [[[MFMailComposeViewController alloc] init] autorelease];
//Set the message, subject, etc...
//Display
[someViewController presentViewController:mailController animated:YES completion:nil];
//As a proof of concept, close programmatically after a couple of seconds...
[self performSelector:#selector(dismissMailController) withObject:nil afterDelay:2.0];
}
-(void)dismissMailController
{
[someViewController dismissViewControllerAnimated:YES completion:nil];
}

UISplitViewController - Pop master when detail is popped (and vice versa)

I have this UISplitViewController which both master and detail VCs are UINavigationController subclasses.
The two are supposed to work "in synchrony", ie, when one pushes a new VC, the second has to push one too. When one pops, the other has to pop too. One always triggers the same action to the other.
I'm already able to handle the pushing part of the problem, since the push functions are explicit in each class I use.
Popping, on the other hand, has been a big problem. The action is triggered when user presses the back button, and I don't know how to detect this event. One possible solution is detecting the event.
Another solution I thought of was to override UINavigationController's - popViewControllerAnimated:, making one class pop the other class, just like this:
// On DetailNav
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
// Code to make MasterNav pop
return [super popViewControllerAnimated:animated];
}
// On MasterNav
- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
// Code to make DetailNav pop
return [super popViewControllerAnimated:animated];
}
I didn't bother adding the full code because this is enough to notice that this approach would cause an infinite-loop, eventually popping both NavControllers to their roots (and then possibly crashing).
What is the best way to achieve the desired behavior?
For iOS 5+, - (BOOL)isMovingFromParentViewController does the trick:
// Use this in the detail VC
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.isMovingFromParentViewController) {
// the view is being popped.
// so also pop the master VC
}
}
I found the solution on #Chrizzz's answer to another question.
Basically you need two subclasses of UINavigationController, one for master and one for detail.
In both subclasses, you must include UINavigationBarDelegate and set the delegate to self
. Then include the following method:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
[[[[self splitViewController] viewControllers][0 or 1] navigationController] performSelector:#selector(popViewControllerAnimated:) withObject:#YES afterDelay:0];
return YES;
}
In the master, you'll want to pop the detail VC, so put a 1 on the index.In the detail, you'll want to pop the master VC, so put a 0 on the index.
This solution allows you to run a routine before popping the view controller.
Update
I was getting some NavigationBar errors getting corrupted such as nested pop animation can result in corrupted navigation bar. So instead of directly calling popViewControllerAnimated: I called performSelector: with zero delay and nothing bad happens now when I pop my views.

Call Delegate Method Prior to View Dismissal

I have a modal view controller presented and just before I dismiss it, I need to call a delegate method which tells the parent view controller to update. (As methods like viewWillAppear are not called when dismissing a modal view controller).
So my code looks like this:
[delegate addEquipmentDidSave:YES];
[self dismissViewControllerAnimated:YES completion:nil];
Very simple. Send a message back, saying, update now! And then just dismiss the view. However, while both of these lines are called, the delegate method never runs. So I check that the delegate it set correctly. When I present the modal view I set the delegate, so its all connected.
Its as if the delegate method isn't getting a chance to run before the view is dismissed. Is this possible? What do you think might be the issue?
Thanks.
Before calling your delegate method first check whether it's available or not
if ([self.delegate respondsToSelector:#selector(addEquipmentDidSave:)] )
{
NSLog("Yes it's available");
[self.delegate addEquipmentDidSave:YES];
}
[self dismissViewControllerAnimated:YES completion:nil];
Do you see that last parameter, the one called completion? That block is called after the view controller is dismissed. Do what you want to do in there.

dismissViewControllerAnimated completion block is not called

I'm trying to dismiss a view controller like this:
[composeViewController dismissViewControllerAnimated:YES completion:^{
NSLog(#"Hello"); // Never outputted
}];
The view controller is dismissed, but for some reason the completion block is never called.
I have never had any issues with completion block not being called with other view controllers.
This view controller is "special" though, because it's added as a child view controller (which I have not worked with previously in my app). Does this impose any side effects why the completion block is not called?
It's added like this:
UIViewController *rootVC = [UIApplication sharedApplication].delegate.window.rootViewController;
[rootVC addChildViewController:self];
[rootVC.view addSubview:self.view];
[self didMoveToParentViewController:rootVC];
Found out what the issue was: the 3rd party view controller I was using had overridden - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion without actually calling completion()
If you present a modal, is the view controller that receive the message (or the top in hierarchy , I still didn't get that) that handles all the process of adding the child v.c. swapping view etc. It seems that you are doing a mix of the two techniques. Just use one. So present it using - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion ad dismiss it using dismissViewController let the view controller manages everything.

Resources