ios 8 : ABPeoplePickerNavigationController dismiss on implementing people picker delegate methods - objective-c

This is strange behavior noticed while accessing contact details from address book in ios 8.
My scenario is simple
Show contacts table
select a row that will invoke didSelectPerson method
in didSelectPerson method
push SecondViewController
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person;
{
SecondViewController *detailVC = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[detailVC.view setBackgroundColor: [UIColor redColor]];
// [peoplePicker.navigationController pushViewController:detailVC animated:YES];
[peoplePicker pushViewController:detailVC animated:YES];
}
but what happens is ABPeoplePickerNavigationController dismiss.
Please enlighten me on this.

I don't know the philosophy thing what happens under the hood of the "didSelectPerson" method, me was facing the same problem today. I found a simple solution to overcome this issue, i override the
"-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion" method of the "ABPeoplePickerNavigationController". Then implement it like somewhat following
bool dismissedEnabled;
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
if (dismissedEnabled) {
[super dismissViewControllerAnimated:flag completion:completion];
}
}
then inside the "didSelectPerson" i have done the following
viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:[NSBundle mainBundle]];
dismissedEnabled = false;
[self presentViewController:viewController animated:YES completion:nil];
this works for me, hope you guys overcome it too :)

It automatically dismisses if you select a contact with a single email address for example.
If a contact has more than one email, you must specify a predicate that will force the ABPeoplePickerNavigationController to push a ABPersonViewController on the stack.
if ([picker respondsToSelector:#selector(setPredicateForSelectionOfPerson:)])
{
// The people picker will select a person that has exactly one email address and call peoplePickerNavigationController:didSelectPerson:,
// otherwise the people picker will present an ABPersonViewController for the user to pick one of the email addresses.
picker.predicateForSelectionOfPerson = [NSPredicate predicateWithFormat:#"emailAddresses.#count = 1"];
}

I believe the default behavior in iOS 8 is that the ABPeoplePickerNavigationController is automatically dismissed when didSelectPerson is called.
The reason that the SecondViewController is not displayed (I'm inferring that this is the problem symptom) is because you are trying to push the SecondViewController while the ABPeoplePickerNavigationController is being dismissed. This overlapping animation is a problem that the iOS core view management/animation system tries to avoid.
You may get a warning in the console when this happens.
#Ratul's solution works because it circumvents this default behavior.
In my scenario, my code sleeps a second before presenting a UIAlertController from within didSelectPerson. This is a hack that depends on the ABPeoplePickerNavigationController dismissal animation taking less than a second. For me, if this alert is not displayed, nobody would even notice this was a problem.
If you want something more robust, you may want to override viewDidAppear to handle this special case (using a flag in your presenting view controller). But this gets a bit clumsy as well.

Related

tvOS preferredfocusedview is not always called

After a viewcontroller has been presented modally, the initial preferredfocusedview is called. However, after we dismiss the viewcontroller and it has been dealloc. preferredfocusedview is not called after presenting the viewcontroller again. Running on tvOS 9.2.
Even adding the following did not help:
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setNeedsFocusUpdate];
[self updateFocusIfNeeded];
}
Anyone know what's going on? Or if there's anyways to debug this?
Edit:
the way I am adding the viewcontroller:
viewController = [[UIViewController alloc] init];
[viewController addChildViewController:self];
[viewController.view addSubview:self.view];
[self didMoveToParentViewController:viewController];
If you are using a container view, having multiple ViewControllers or adding only one View Controller, the preferredFocusEnvironments method must be called from the rootView Controller indicating which View Controller to focus.
For eg.
View Controller A has a container View having ViewControllers B and ViewController C inside the Container.
View Controller A should have preferredFocusEnvironments returning which ViewController to focus.
This way, preferredFocusEnvironments on ViewController B or ViewController C will be called whenever the view becomes visible.
If the ViewController A doesn't have preferredFocusEnvironments, then it won't be called on the containerView ViewControllers.
Implementing custom focus behavior in tvOS 9 is disaster. Apple already mentioned that there is a limitation on redirecting focus specially when presenting/ dismissing a viewcontroller in WWDC.
tvOS10 will handle munch better with preferredFocusEnvironments.
https://developer.apple.com/videos/play/wwdc2016/215/
When I needed to fix this focus redirection issues in viewDidAppear in tvOS 9, I had exactly same issues. Sometimes it works, sometimes not. No clue what so ever. But after I put split second delay on setNeedsFocusUpdate / updateFocusIfNeeded in viewDidAppear it was way better in terms of consistency. preferredFocusedView get called all the time.
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self setNeedsFocusUpdate];
[self updateFocusIfNeeded];
});
}
Do this in both presented and presenting view controllers, if you are manually changing focus. This is all from my observation and I don't think there is a proper way to achieve some focus behavior because tvOS API is kind of new and premature. Sorry about not being able to give you good explanation why this might work. Good luck.

Troubles with UISearchBar \ UISearchDisplayViewController

I'm having a hard time with my SearchDisplayViewController on iOS 7.
I have a searchBar hidden over a UITableViewController, like
self.tableView.tableHeaderView = searchBar;
Problem is that when I tap on the searchBar to type in something, then the view starts greying out, and I quickly tap the screen in a random point to dismiss it, coming back to the tableView, the searchBar disappears. Totally. Only on iOS 7 though.
Debugging it, the frame is always the same: 0,0,320,44. But the bar is invisible!
Also tried to do
self.tableView.contentOffset = CGPointMake(0,self.searchDisplayController.searchBar.frame.size.height);
still disappears when I do it quickly.
On iOS 6 it works just fine. Problem is only with iOS 7 as far as I'm seeing.
I don't know what it depends on, has anyone encountered the same problem I have?
As of Double tap UISearchBar with search delegate on iOS 7 causes UISearchBar to disappear, I found the workaround to actually work and solved the bug - for now.
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
I encountered the same issue, and noticed that searchDisplayControllerDidEndSearch was being called twice. The first time, the superview of self.searchDisplayController.searchBar is the UITableView, and the second time it's still a UIView.
With the accepted answer, I worry about unintended consequences or unneeded overhead from re-inserting the subview every time the search bar is double-tapped, and I also worry about it breaking with future iOS versions. Fortunately, we can take advantage of the superview strangeness like this:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
if (self.tableView != self.searchDisplayController.searchBar.superview) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
If I had to guess what was happening, the UISearchBar is automatically creating a temporary UIView as its superview when it's active – this is the view seen when the search is being performed. While the UISearchBar is being dismissed, the superview gets set back to be the UITableView it had before, unless it gets dismissed so quickly that it was never properly initialized, in which case it cleans up improperly and the UITableView never gets the UISearchBar back as its child.
This solution still isn't ideal, and I think Apple must be doing something different in its own apps because their search bar UX feels a bit better. I think it would be better not to handle the second tap in the first place until the UISearchBar was ready. I tried using the other UISearchBarDelegate methods to do this, but I couldn't find an appropriate hook to override the current behavior.
I had the same problem with iOS 7 and I solved it from the apple documentation. The error most people do is that they associate the UISearchBar variable to the self.searchDisplayController.searchBar as the same...! NO NO..! They are 2 different things!!! UISearchBar should be declared and initialized and then wrapped into self.searchDisplayController as searchBar then later wrapped into self.tableView.tableHeaderView by so doing it will not disappear!!!
self.searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchDisplayController.delegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.searchResultsDelegate = self;
[self.searchBar setPlaceholder:#"search the hell in me"];
self.tableView.tableHeaderView = self.searchDisplayController.searchBar;
More refined approach for #lehrblogger solution:
- (void)addSearchDisplayControllerBackToTableView {
if ([self.searchDisplayController.searchBar isDescendantOfView:self.tableView] == NO) {
NSLog(#"Search bar is not in current table view, will add it back");
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
[self.searchDisplayController setActive:NO animated:YES];
}
}
Reason for this approach: While searching the search bar is moved to search container and the superview of search bar is always some other view other than current table view.
Note: This will dismiss the search, because user tapped more than once on search bar.

Application tried to present modally an active controller : UIImagePickerController

I'm struggle at this for 2 days and believe that this is the moment I should call for help. After I search SOF for a while, none of any answer could solve my problem. Here are my application ...
In the application,
Device is iPad, iOS 6
RootViewController is NavigationController
TopViewController is TabBarController
In this TabBarController, I present a popoverController from right bar button of navigation bar
In presenting popover there is a button to allow user to pick image from by taking new one or pick from existing.
To pick new one, I presentViewController UIImagePickerController to allow user to take photo with divice camera. presentModalViewController:animated: if iOS < 6, and presentViewController:animated:completion: for iOS > 6
I also hide Status Bar before presentation
To select from existing photo, I do presentPopoverFromBarButtonItem:permitArrowDirections:animated:
PopoverViewController also referencing by A TabBarController
Here is the issue
Present UIImagePickerController will always failed if user try to pick new one first with exception "Application tried to present modally an active controller <[name of view controller that try to present]>"
BUT, if user try to pick image from camera roll for once and then try to take new one again, it won't fail.
Here are what I tried
present from RootViewController
present from TopViewController (TabBarController)
present from popoverViewController itself
present from a tab of TabBarController
hide popoverViewController before presentation
resignFirstResponder from a textField in popoverViewController
Here is the current code I'm using
// PopoverViewController, presented by a tab in TabBarController
- (IBAction)takePhoto:(id)sender {
[self.delegate takePhotoWithDeviceCamera];
}
// A Tab in TabBarController, delegate of popoverViewController
- (void)takePhotoWithCamera {
[[UIApplication sharedApplication] setStatusBarHidden:YES];
if ([UIDevice OSVersion] < 6.0) {
[self presentModalViewController:cameraPicker animated:YES];
} else {
[self presentViewController:cameraPicker animated:YES completion:nil];
}
}
Any idea what would cause this error? Any suggestion are welcome. Thank you.
Got the same trouble than you and finally got the solution based on #CainaSouza's answer. I've been working with Xamarin.iOS so I'll make my answer in C#, but it can be easily translated to Objective-C.
I'm using the same code as #CainaSouza to call the controller:
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController (customController, true, null);
And then I add the following code to my custom RootViewController:
public override void PresentViewController (UIViewController viewControllerToPresent, bool animated, Action completionHandler)
{
if (PresentedViewController != viewControllerToPresent) {
base.PresentViewController (viewControllerToPresent, animated, completionHandler);
}
}
The trick is to check if you haven't presented that UIViewController before.
I know it's an old question, but hope it will help someone. :)
Present the imagePicker controller in a popoverController(in case of iPad). This will not give you that error.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:picker];
[popover presentPopoverFromRect:self.selectedImageView.bounds inView:self.selectedImageView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
self.popOver = popover;
}
else {
[self presentModalViewController:picker animated:YES];
}
Best Regards.
Have you tried to present it like this?
[self.view.window.rootViewController presentModalViewController:cameraPicker animated:YES];
My guess is that the cameraPicker instance is not correctly allocated/released. Try creating the cameraPicker inside your - (void)takePhotoWithCamera method rather than relying on a previously created instance. You'll get a handle to the picker instance in the callback methods...
I had the same problem - I wanted users to take photos using a full screen view (i.e. call presentViewController and pass UIImagePickerController controller instance) and select existing photos from a popover (I associated it with a popover using initWithContentViewController). I reused the same instance of UIImagePickerController for both camera and popover and it threw the same exception if I tried to run a camera before opening a popover.
I turned out to cause a problem and my solution was simply to have two instances of UIImagePickerController - one for camera (which I presented from a main view) and another one for popover. It works so far. :-)
Not sure if it is still actual for the original poster, but hopefully it will help anyone else who encounter this discussion.

Admobs sometimes appear twice on different locations

We are adding admobs as a backup ad-system for countries without iAd. But sometimes the ads appear twice, the second ad sometimes in the middle of the screen about 100-200 px above the other ad. It seems like the second ad only appears when visiting a view that already has an ad. It kinda feels like the ad-call is in viewDidAppear instead of viewDidLoad, but we don't. Or didFailToReceiveAdWithError gets called on outside viewDidLoad-call for ad. Anyone know anything about this?
EDIT:
They CAN appear both at once, without having to revisit a view that already has an ad.
EDIT(code)
basically:
-(void) viewDidLoad
{
[self createAdBannerView];
[self.view addSubview:bannerView];
}
-(void) createAdBannerView
{
bannerView = [[AdBannerView alloc] initWithFrame:CGRectZero];
CGRect bannerFrame = bannerView.frame;
bannerView.frame = bannerFrame;
bannerView.delegate = self;
bannerView.requiredContentSizeIdentifiers = [NSSet setWithObjects:ADBannerContentSizeIdentifiersPortrait,AdBannerContentSizeIdentifierLandscape,nil];
}
-(void)bannerView:(AdBannerView*)banner didFailToReceiveAdWithError:(NSError *)error
{
[self.bannerView removeFromSuperview];
GbannerView = [[GADBannerView alloc] initWithFrame:CGRectMake(0.0, self.view.frame.size.height-99, 320,50)];
self.GbannerView.adUnitID=#"xxxxxxx";
self.GbannerView.rootViewController = self;
[self.view addSubview:self.GbannerView];
[self.GbannerView loadRequest:[GADRequest request]];
}
Also, if someone knows: In views containing tableViews, the locations of the admobs is wrong. It gets stuck behind a tabbar, even though the location takes this into account. This does not happen in a normal UIView. whatz..
I'd recommend you pull out the tableView question into its own question. There's an example of how to do this here, but I don't know if that satisfies your specific case).
Is there a reason you're not just using AdMob mediation here? They've got support for iAd and setting it up with client code is pretty easy.
For your specific case, I wonder if bannerView:didFailToReceiveAdWithError: is getting continuously called even after you remove AdBannerView from its superview. Why don't you create one GADBannerView and AdBannerView at the start in viewDidLoad:, then check if it exists before instantiating any new ones?

"Application tried to present modally an active controller"?

I just came across a crash showing a NSInvalidArgumentException with this message on an app which wasn't doing this before.
Application tried to present modally an active controller
UITabBarController: 0x83d7f00.
I have a UITabBarController which I create in the AppDelegate and give it the array of UIViewControllers.
One of them I want to present modally when tapped on it. I did that by implementing the delegate method
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
If that view controller is of the class of the one I want to present modally, I return NO and do
[tabBarController presentModalViewController:viewController animated:YES];
And now I'm getting that error, which seems to mean that you can't present modally a view controller that is active somewhere else (in the tabbar...)
I should say I'm on XCode 4.2 Developer Preview 7, so this is iOS 5 (I know about the NDA, but I think I'm not giving any forbidden details). I currently don't have an XCode installation to test if this crashes compiling against the iOS4 SDK, but I'm almost entirely sure it doesn't.
I only wanted to ask if anyone has experienced this issue or has any suggestion
Assume you have three view controllers instantiated like so:
UIViewController* vc1 = [[UIViewController alloc] init];
UIViewController* vc2 = [[UIViewController alloc] init];
UIViewController* vc3 = [[UIViewController alloc] init];
You have added them to a tab bar like this:
UITabBarController* tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:[NSArray arrayWithObjects:vc1, vc2, vc3, nil]];
Now you are trying to do something like this:
[tabBarController presentModalViewController:vc3];
This will give you an error because that Tab Bar Controller has a death grip on the view controller that you gave it. You can either not add it to the array of view controllers on the tab bar, or you can not present it modally.
Apple expects you to treat their UI elements in a certain way. This is probably buried in the Human Interface Guidelines somewhere as a "don't do this because we aren't expecting you to ever want to do this".
I have the same problem. I try to present view controller just after dismissing.
[self dismissModalViewControllerAnimated:YES];
When I try to do it without animation it works perfectly so the problem is that controller is still alive. I think that the best solution is to use dismissViewControllerAnimated:completion: for iOS5
In my case i was trying to present the viewController (i have the reference of the viewController in the TabBarViewController) from different view controllers and it was crashing with the above message.
In that case to avoid presenting you can use
viewController.isBeingPresented
!viewController.isBeingPresented {
// Present your ViewController only if its not present to the user currently.
}
Might help someone.
The same problem error happened to me when I tried to present a child view controller instead of its UINavigationViewController parent
I had same problem.I solve it. You can try This code:
[tabBarController setSelectedIndex:1];
[self dismissModalViewControllerAnimated:YES];
For React Native Developer - Problem might not be in AppDelegate Or main.m if app has been successfully build and is running and will crash after splash or perhaps the error screen
Issue might be due to use of fonts/resources that is not available with xcode and not properly configured.. You can find out the error by commenting certain portion starting from App.js and drilling inside the navigation/screens and commenting the components till you find the component that is generating the error....
In my case the resource of fontFamily was making an issue which was used right after splash in walkthrough screen
<Text style={{fontFamily: Fonts.roboto}}>ABC</Text>
Here font roboto wasnot configured properly. Wasted entire days just debugging the error hope its helps you
In my case, I was presenting the rootViewController of an UINavigationController when I was supposed to present the UINavigationController itself.
Just remove
[tabBarController presentModalViewController:viewController animated:YES];
and keep
[self dismissModalViewControllerAnimated:YES];
Instead of using:
self.present(viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?)
you can use:
self.navigationController?.pushViewController(viewController: UIViewController, animated: Bool)
This is my way which supporting multiple Windows(from a single APP) on the iPad and nested modal present.
import UIKit
///✅Use this public method
public func SheetViewController(ViewController:UIViewController) {
for i in returnAvailableViewControllers().shuffled() {
if i.presentedViewController == nil && !ViewController.isViewLoaded {i.present(ViewController, animated: true, completion: {})}
}
}
///Returns all possible ViewControllers
private func returnAvailableViewControllers() -> [UIViewController] {
let 场景 = UIApplication.shared.connectedScenes
var 存储VC : [UIViewController] = []
for i in 场景 {
if i.activationState == .foregroundActive {
//⭐️Set up “foregroundActive” to give the user more control
var 视图控制器 = (i.delegate as? UIWindowSceneDelegate)?.window??.rootViewController
if 视图控制器 != nil {
存储VC.append(视图控制器!)
}
var 结束没 = true
while 结束没 {
//🌟Enumerate all child ViewController
视图控制器 = 视图控制器?.presentedViewController
if 视图控制器 != nil {
存储VC.append(视图控制器!)
} else {
结束没.toggle()
}
}
}
}
return 存储VC
}