iOS UIActionSheet for NSDataDetector and double modal view - objective-c

I am having an issue with the detected numbers in iOS 7 and iPhone, when the user long tap the number detected by the OS, it prompts an UIActionSheet with the options: "Call", "Send Message", "Add to Contacts", "Copy" and "Cancel". The problem I am facing is, when the option "Send Message" or "Add to Contacts" is tapped, the OS creates an modal view on top of my current modal view, which leads to having the navigation bar of the second modal view not being displayed correctly.
With that in mind, I am not able to assert at which moment the user has tapped which button, because it is not me who created the UIActionSheet (iOS does itself), then I can not receive any kind of delegate methods. The only message sent to the UIViewController is:
-(BOOL)textView:(UITextView *)textView
shouldInteractWithURL:(NSURL *)URL
inRange:(NSRange)characterRange
Which tells me what kind of data was tapped once by the user (but not long tapped). I tried as well with the method call:
-(void)viewWillDisappear:(BOOL)animated
-(void)viewDidDisappear:(BOOL)animated
Unfortunately, they are never invoked on iOS7, whereas iOS8 does. Which drives me to the conclusion that, so far, this issue is only iOS7 related, I am using an iPhone 4 with iOS 7.1.2. When I tried the same case in iOS8, the second modal view renders correctly, being placed on top of my current view.
I hope someone has more info or other ideas.
Thanks!!

The solution that I ended up applying was to completely avoid the long press gesture on the title number that was generating the action sheet to appear. I did it checking on the gesture recognizer list for the UITextView that was inside the cell, then look up for one that was of the class UILongPressGestureRecognizer, inside I would just disable the friend gesture. Snippet of code is:
RestrictedTextView.h
#import <UIKit/UIKit.h>
#interface RestrictedTextView : UITextView
#end
RestrictedTextView.m
#import "RestrictedTextView.h"
NSString *const kFriendsStringInGesture = #"friends";
#implementation RestrictedTextView
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([[gestureRecognizer class] isEqual:[UILongPressGestureRecognizer class]])
{
UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)gestureRecognizer;
longPress.enabled = YES;
if ([longPress valueForKey:kFriendsStringInGesture] != nil)
{
UILongPressGestureRecognizer *friendLongPress = (UILongPressGestureRecognizer *)[[longPress valueForKey:#"friends"] anyObject];
friendLongPress.enabled = NO;
}
}
return YES;
}
#end

Related

How to perform IBAction before dismiss sheetViewController from same UIButton?

I am writing a Cocoa Mac app in Objective-C and using Storyboard for my UI.
I have a "Confirm" button in my sheetViewController.m which I want to perform some action (save some settings) as well as dismiss the sheetViewController at the same time. They both use the sheetViewController.m as outlets.
Unfortunately, with Storyboard, I can only pick one received action (IBAction) or dismissController.
I want to perform the IBAction FIRST, before dismissing the sheet. How can I accomplish this?
Happy to do this in code as well instead of Storyboard if necessary!
Thanks!
You can use codes to dismiss controller in your IBAction.
I found out how to reference Storyboard ID in code:
On MainViewController:
- (IBAction)didClickButton:(NSButton *)sender {
SheetViewController *sheetViewController = [self.storyboard instantiateControllerWithIdentifier:#"SheetViewController"];
// Must match Storyboard ID
[self presentViewControllerAsSheet:sheetViewController];
}
On SheetViewController:
- (IBAction)didClickButton:(NSButton *)button {
// Do something
[self dismissViewController:self];
}

Gesture Recognizer not working on iPad but working on iPhone, Why?

Why does this method not work on iPad?
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
NSLog(#"should receive");
return YES;
}
I have copied the same VC from iPhone Storyboard to iPad storyboard.
The code is simple, and all I'm trying to do is get the NSlog to appear.
While this is working on the iPhone simulator, it is not working on the iPad simulator (or a real device, checked with iPad 4).
Any suggestions as to what I should do to make it work on the iPad?
I am trying to work out what I have missed but nothing comes to mind.
STORYBOARD
IF you added your gesture recognizer in the storyboard.
Check that you set it as a delegate ..by right clicking the recognizer and dragging the mouse into the view controller. Pop up will show it clearly.
IN CODE
If the recognizer is added in code..then simply make sure you conform to protocol
and set its delegate.
#interface ViewController : UIViewController <XXXGestureRecognizerDelegate>
-(void)viewDidLoad
{
self.someGestureRecognizer.delegate = self;
//other code
}

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.

subview from UIWindow and sending it back to UIwindow

I have an UIWebView loaded with div, act as editor to write. Now i am adding UIWebView as sub view on UIWindow to set the frame equal to full screen and to hide the UIKeyboard, but at some button method, i need to get UIWebview from UIWindow and sent it back to UIKeyboard. Here is my code which is not working:
keyboardWindowFrame= nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows])
{
if (![[testWindow class] isEqual:[UIWindow class]])
{
keyboardWindowFrame = testWindow;
[webViewForEditing setFrame:CGRectMake(5, 63, 310, 400)];
[webViewForEditing.scrollView setContentSize:CGSizeMake(310, 400)];
[keyboardWindowFrame addSubview:webViewForEditing];
break;
}
}
- (IBAction)keyboardButtonSelected:(id)sender
{
[keyboardWindowFrame sendSubviewToBack:webViewForEditing]; //need to send UIWebView back to UIWindow so i can write
}
I think I am trying to do the same thing as you and so although your question is not particularly clear hopefully my answer will help.
It seems that you have a view 'webViewForEditing' which you want to add and bring in front of the keyboard. When you click a button you want to put this view behind the keyboard again.
I have also tried using the sendSubViewToBack code with no joy.
In the end though I managed to get it to work using:
[[self view] exchangeSubviewAtIndex: withSubviewAtIndex: ];
(Credit goes to Sunny with it adapted from a question here)
I used the following code below, with to switch between a UIView and the keyboard:
- (IBAction)toggleButtonPressed:(id)sender {
UIWindow * window = [UIApplication sharedApplication].windows.lastObject;
if (window.subviews.count == 1) {
[window addSubview:_menuView];
}
else {
[window exchangeSubviewAtIndex: 0 withSubviewAtIndex: 1];
}
}
I am only working with two views (_menuView and the keyboard) which means I check how many subviews my window has to make sure I only add _menuView once.
Then it is easy to exchange the two views.
Even if you had more subviews in your window I am sure you could use a modified version of this. As long as none of your other views change places then exchanging them will always switch the same two views.
NOTE: As an aside. I am not sure if this code works if called when the keyboard is not first responder. I get the variable for Window using the last object in the window, which is always the keyboard if it has been made first responder. It might need tweaking to get it working at other times.
I formulated another, more eloquent, answer while working on this:
Add the views you want to the window view that holds the keyboard view:
#interface BViewController : UIViewController {
UIWindow * window;
}
// Add the code when the keyboard is showing to ensure it is added while we have a keyboard and to make sure it is only added once
-(void) keyboardDidShow: (NSNotification *) notification {
window = [UIApplication sharedApplication].windows.lastObject;
if (window.subviews.count == 1) {
[window addSubview:_menuView];
[window addSubview:_gameView];
[window addSubview:_stickerView];
[self hideViews];
}
}
- (void)hideViews {
_menuView.hidden = YES;
_gameView.hidden = YES;
_stickerView.hidden = YES;
}
So now we have our UIWindow view which contains our views and also our keyboard. They are all hidden so our keyboard appears at the front and can be typed on.
Now use a simple function to decide which view to bring in front of the keyboard:
- (void)bringViewToFront: (UIView *)view {
[self hideViews];
view.hidden = NO;
}
Hiding the views makes sure we only have our correct view at the front.
I spent a lot of time thinking how we could move different views forward and back, using the exchange function. Actually using hide and reveal means we can get our view immediately and still easily have access to the keyboard by hiding all the views.

TextField will NOT resign first responder with UIModalPresentationFormSheet view

I've created a button on one viewController that loads another view modally using the UIModalPresentationFormSheet presentation style. On this loaded view, I have two textFields, and I'm forcing the first textField to be first responder so that the keyboard will appear immediately with the new view. I've set up the textFields to have an action method that is hooked up to "Did End on Exit" event. However, whenever I hit "return" on the keyboard for either textField, the keyboard fails to go away (Here is my code):
// addCustomPage method that is called when button from original view is touched
- (IBAction) addCustomPage:(id)sender
{
NSLog(#"Adding Custom Page");
if (!self.customPageViewController)
{
self.customPageViewController =
[[CustomPageViewController alloc] initWithNibName:#"CustomPageViewController" bundle: nil];
}
customPageViewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:customPageViewController animated:YES];
// force keyboard to appear with loaded page on the first textField
[customPageViewController.firstTextField becomeFirstResponder];
}
#interface CustomPageViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *firstTextField;
#property (strong, nonatomic) IBOutlet UITextField *secondTextField;
- (IBAction)keyboardEndOnExit:(id)sender; // DID END ON EXIT EVENT
#end
//in CustomPageViewController.m
-(IBAction)keyboardEndOnExit:(id)sender
{
[sender resignFirstResponder];
}
This is a fairly straight forward problem, and I have no problem normally dismissing keyboards using this technique with basic views and textFields. I'm not sure if using a view in this presentation format, or set up makes things different. Thanks!
You have confirmed that you keyboardEndOnExit method is actually being called?
You could also take a more direct approach by calling [yourTextView resignFirstResponder] when a specific action is take by the user, such as a key pressed etc. I would still check if that method is ever being called using breakpoints or a log.
Have a look at this question. Pretty sure it is the same problem caused by UIModalPresentationFormSheet.