UIMenuController keep resetting after first time - ios

I'm using the following code to display custom items in the UIMenuController when selecting text within a UIWebView.
//Custom UIMenuController actions for highlighting and commenting
UIMenuItem *highlightItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Highlight", nil) action:#selector(highlightAction:) image:[UIImage imageNamed:#"HighlightIcon"]];
UIMenuItem *commentItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"Comment", nil) action:#selector(createCommentAction:) image:[UIImage imageNamed:#"CommentIcon"]];
[UIMenuController sharedMenuController].menuItems = #[highlightItem, commentItem];
[[UIMenuController sharedMenuController] update];
It works fine the first time and display only the two custom options I have, but after that whenever some text is selected it shows the native options first and the custom ones on the next page of items.
I'm wondering how to permanently remove the native options - all other questions and examples don't seem to work for iOS 7+.
I also have this for enabling the menu:
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(highlightAction:) ||
action == #selector(createCommentAction:) ||
return YES;
else if (action == #selector(copy:))
{
return NO;
}
return [super canPerformAction:action withSender:sender];
}
#pragma mark - privates
- (void)pressme:(id)sender
{
[[UIMenuController sharedMenuController] setTargetRect:[sender frame] inView:self.view];
[[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES];
[[UIMenuController sharedMenuController] update];
}

To disable default items, in method canPerformAction:withSender: instead of call super canPerformAction, just return NO.
Here is simple example how it should be for one action (just tried it, and it works):
// Let say we have textField property outlet
// Action to display menu
- (IBAction)showMenu:(id)sender {
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:#"test" action:#selector(test:)];
[UIMenuController sharedMenuController].menuItems = #[item];
[[UIMenuController sharedMenuController] update];
[self.textField becomeFirstResponder];
UIMenuController *theMenu = [UIMenuController sharedMenuController];
[theMenu setTargetRect:self.textField.frame inView:self.textField.superview];
[theMenu setMenuVisible:YES animated:YES];
[[UIMenuController sharedMenuController] update];
}
// Enable only "test:" selector here, disable others
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == #selector(test:))
return YES;
return NO;
}

Related

Select a textView without select text inside?

I have a UITextView inside of UITableViewCell. That UITextView I want be selectable but not text to. When the Menu Controller is appearing and i want to perform copy action, copy all text inside not just a part.
I want something like messenger app :
For the moment i have that :
Thank you in advance!
In fact it's not quite so easy, as on the road show up some strange stuff, but I managed to create a customized one.
The steps are as following:
Create a subclass of UITextView
Override canPerformAction:
withSender: for filtering the default actions of the menu
that pops up.
Configuring textView in order not to be able to edit or
select.
Editing UIMenuController items which provide the actions and buttons on the menu
Add different selectors for each UIMenuItem **** (That is because the sender of the selector is not an UIMenuItem, but a UIMenuController which leads to another matter. Check here for more info. A gist by me for that)
Code
No more talking so here is the code:
EBCustomTextView.h
//
// EBCustomTextView.h
// TestProject
//
// Created by Erid Bardhaj on 3/8/16.
// Copyright © 2016 Erid Bardhaj. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, EBCustomTextViewMenuAction) {
EBCustomTextViewMenuActionCopy,
EBCustomTextViewMenuActionDefine,
EBCustomTextViewMenuActionRead
};
#class EBCustomTextView;
#protocol EBCustomTextViewMenuActionDelegate <NSObject>
- (void)customTextView:(EBCustomTextView *)textView didSelectMenuAction:(EBCustomTextViewMenuAction)action;
#end
#interface EBCustomTextView : UITextView
#property (weak, nonatomic) id<EBCustomTextViewMenuActionDelegate> menuActionDelegate;
#end
EBCustomTextView.m
//
// EBCustomTextView.m
// TestProject
//
// Created by Erid Bardhaj on 3/8/16.
// Copyright © 2016 Erid Bardhaj. All rights reserved.
//
#import "EBCustomTextView.h"
#implementation EBCustomTextView {
EBCustomTextViewMenuAction currentAction;
}
- (void)awakeFromNib {
[super awakeFromNib];
// Configure the textView
self.editable = NO;
self.selectable = NO;
}
#pragma mark - Datasource
- (NSArray *)items {
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:#"Copy" action:#selector(copyMenuItemPressed)];
UIMenuItem *item1 = [[UIMenuItem alloc] initWithTitle:#"Define" action:#selector(defineMenuItemPressed)];
UIMenuItem *item2 = [[UIMenuItem alloc] initWithTitle:#"Read" action:#selector(readMenuItemPressed)];
return #[item, item1, item2];
}
#pragma mark - Actions
- (void)copyMenuItemPressed {
if ([self.menuActionDelegate respondsToSelector:#selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionCopy];
}
}
- (void)defineMenuItemPressed {
if ([self.menuActionDelegate respondsToSelector:#selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionDefine];
}
}
- (void)readMenuItemPressed {
if ([self.menuActionDelegate respondsToSelector:#selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionRead];
}
}
#pragma mark - Private
- (void)menuItemPressedAtIndex:(NSInteger)index {
currentAction = index;
if ([self.menuActionDelegate respondsToSelector:#selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:currentAction];
}
}
#pragma mark Helpers
- (void)showMenuController {
UIMenuController *theMenu = [UIMenuController sharedMenuController];
theMenu.menuItems = [self items];
[theMenu update];
CGRect selectionRect = CGRectMake (0, 0, self.contentSize.width, self.contentSize.height);
[theMenu setTargetRect:selectionRect inView:self];
[theMenu setMenuVisible:(theMenu.isMenuVisible) ? NO : YES animated:YES];
}
#pragma mark - Overridings
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
// Filter any action for this textView except our custom ones
if (action == #selector(copyMenuItemPressed) || action == #selector(defineMenuItemPressed) || action == #selector(readMenuItemPressed)) {
return YES;
}
return NO;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
if ([theTouch tapCount] == 1 && [self becomeFirstResponder]) {
[self showMenuController];
}
}
#end
Implementation
Set your textView's class to EBCustomTextView and conform to EBCustomTextViewMenuActionDelegate
Interface
#interface ViewController () <EBCustomTextViewMenuActionDelegate>
viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.textView.menuActionDelegate = self;
}
Protocol conformance
#pragma mark - Delegation
#pragma mark EBCustomTextViewMenuActionDelegate
- (void)customTextView:(EBCustomTextView *)textView didSelectMenuAction:(EBCustomTextViewMenuAction)action {
switch (action) {
case EBCustomTextViewMenuActionCopy:
NSLog(#"Copy Action");
break;
case EBCustomTextViewMenuActionRead:
NSLog(#"Read Action");
break;
case EBCustomTextViewMenuActionDefine:
NSLog(#"Define Action");
break;
default:
break;
}
}
Output
Enjoy :)
you have to use
[UITextView selectAll:self];
or (with specific range)
UITextView.selectedRange = NSMakeRange(0, 5);
or add txtview.delegate = self;
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
dispatch_async(dispatch_get_main_queue(), ^{
[textView selectAll:nil]; //nil or self as per needed
});
return YES;
}
Use UILongPressGestureRecognizer Gesture
#import "ViewController.h"
#interface ViewController ()<UIGestureRecognizerDelegate>
{
UITextView * txtV;
UILongPressGestureRecognizer * longPressGestureRecognizer;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
txtV=[[UITextView alloc]initWithFrame:CGRectMake(20, 50, 280, 300)];
txtV.text = #"Jai Maharashtra";
txtV.userInteractionEnabled=YES;
txtV.selectable=NO;
txtV.editable=NO;
txtV.font= [UIFont italicSystemFontOfSize:[UIFont systemFontSize]];
[self.view addSubview:txtV];
longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressGesture:)];
longPressGestureRecognizer.delegate=self;
[txtV addGestureRecognizer:longPressGestureRecognizer];
}
- (void)longPressGesture:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
txtV.selectable=YES;
[txtV selectAll:self];
}
}
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
[txtV selectAll:self];
if (action == #selector(cut:))
{
return YES;
}
if (action == #selector(selectAll:))
{
return YES;
}
if (action == #selector(copy:))
{
return YES;
}
if (action == #selector(delete:))
{
return YES;
}
txtV.selectable=NO;
return YES;
}
First i start with create for every UITextView in cell a UILongPressGestureRecognizer to recognize long press.
In cellForRowAtIndexPath i just add that code :
UILongPressGestureRecognizer *recognizer1 = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPressText1:)];
messageRecognizer.delaysTouchesBegan = YES;
UILongPressGestureRecognizer *recognizer2 = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPressText2:)];
translateRecognizer.delaysTouchesBegan = YES;
[cell.backgroundViewText1 addGestureRecognizer:recognizer1];
[cell.backgroundViewText2 addGestureRecognizer:recognizer2];
I use two different selectors because i need to know which text to get from cell.
After the methods :
-(void)handleLongPressText1:(UILongPressGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil){
NSLog(#"couldn't find index path");
} else {
[self becomeFirstResponder];
UIMenuController *menuController = [UIMenuController sharedMenuController];
UIMenuItem *resetMenuItem = [[UIMenuItem alloc] initWithTitle:#"Copy" action:#selector(copyMethod)];
UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:#"Read" action:#selector(readButtonAction)];
[menuController setMenuItems:[NSArray arrayWithObjects:resetMenuItem,menuItem, nil]];
CGRect rect =gestureRecognizer.view.frame;
NSLog(#"%#", NSStringFromCGRect(rect));
[menuController setTargetRect:gestureRecognizer.view.frame inView:[[gestureRecognizer view] superview]];
[menuController setMenuVisible:YES animated:YES];
}
}
}
-(void)handleLongPressText2:(UILongPressGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil){
NSLog(#"couldn't find index path");
} else {
[self becomeFirstResponder];
UIMenuController *menuController = [UIMenuController sharedMenuController];
UIMenuItem *resetMenuItem = [[UIMenuItem alloc] initWithTitle:#"Copy" action:#selector(copyMethod)];
UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:#"Read" action:#selector(readButtonAction)];
[menuController setMenuItems:[NSArray arrayWithObjects:resetMenuItem,menuItem, nil]];
CGRect rect =gestureRecognizer.view.frame;
NSLog(#"%#", NSStringFromCGRect(rect));
[menuController setTargetRect:gestureRecognizer.view.frame inView:[[gestureRecognizer view] superview]];
[menuController setMenuVisible:YES animated:YES];
}
}
}
And for disable all default items in UIMenuController use above code
- (BOOL)canPerformAction:(SEL)iAction withSender:(id)iSender {
SEL copySelector = NSSelectorFromString(#"copyMethod");
SEL readSeletor = NSSelectorFromString(#"readButtonAction");
if (iAction == copySelector) {
return YES;
}else if (iAction ==readSeletor){
return YES;
}else{
return NO;
}
return NO;
}

UIMenuController Has Zero Frame

I really went through every other question on SO related to this topic. But none helped me so far.
I am trying to show a UIMenuController on a UIView. I therefore subclassed the UIView and implemented all the required methods already.
In the UIViewController I instantiate a UIGestureRecognizer and add it to the custom UIView. This works just fine. I can also handle the long press gesture. It is just that the UIMenuController won't show up!
#interface CopyableView : UIView
#end
#implementation CopyableView
- (BOOL)canBecomeFirstResponder
{
NSLog(#"_CAN BECOME 1. RESPONDER_");
return YES;
}
- (BOOL)canPerformAction:(SEL)action
withSender:(id)sender
{
NSLog(#"_CAN PERFORM ACTION_");
return (action == #selector(copy:));
}
- (void)copy:(id)sender
{
NSLog(#"_COPY_");
}
#end
#interface InfoViewController : UIViewController
#property (nonatomic, weak) IBOutlet CopyableView *myView;
#end
#implementation InfoViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.myView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(handleLongPressGesture:)]];
}
- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan)
{
NSLog(#"__ %i __", [UIMenuController sharedMenuController].menuVisible);
[self.myView becomeFirstResponder];
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:#"Copy"
action:#selector(copy:)];
[[UIMenuController sharedMenuController] setMenuItems:#[item]];
[[UIMenuController sharedMenuController] setTargetRect:CGRectMake(33, 33, 33, 33)
inView:self.myView];
[[UIMenuController sharedMenuController] setMenuVisible:YES
animated:NO];
NSLog(#"__ %i __", [UIMenuController sharedMenuController].menuVisible);
NSLog(#"#### %# ####", NSStringFromCGRect([UIMenuController sharedMenuController].menuFrame));
}
}
Checking [UIMenuController sharedMenuController].menuVisible returns true at the end of the method. So things seem to work. Still I see nothing happening.
The frame of the UIMenuController is {{0, 0}, {0, 0}} which I cannot understand.

Disable double tap popup UITextView

There is a popup comes when double tap on UITextView. You can see it in below image. How can we disable this?
Try this:
txtView.selectable = NO;
Edited:
Over ride this in your view controller to handle, Use this code where you have your UITextfield.
// Hide cut/copy/paste menu
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender {
UIMenuController *menuController = [UIMenuController sharedMenuController];
if (menuController) {
[UIMenuController sharedMenuController].menuVisible = NO;
}
return NO;
}
For iOS 7 & later version you need to do like this:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
}];
return [super canPerformAction:action withSender:sender];
}
Hope it will work for you.

ios 7 - Custom UIMenuItem not working on TableViewCell

I am working on adding custom UIMenuItem on tableViewCell. I used this stackoverflow post to to add customMenuItem. This worked fine on ios 6. But it is not at all working on ios 7.
Below is the implementation I have:
In viewDidLoad:
UIMenuItem *sendByEmailMenuItem = [[UIMenuItem alloc] initWithTitle:#"Send By Email" action:#selector(sendByEmail:)];
[[UIMenuController sharedMenuController] setMenuItems: #[sendByEmailMenuItem]];
[[UIMenuController sharedMenuController] update];
Then adding its delegate
// Shared Menu item delegate actions
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
self.orderAtIndex = [self.orders objectAtIndex:indexPath.row];
[self becomeFirstResponder];
return YES;
}
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
return (action == #selector(sendByEmail:));
}
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
if (action == #selector(sendByEmail:)) {
[self sendByEmail:sender];
}
}
// Subclassing Table View cells
-(BOOL) canPerformAction:(SEL)action withSender:(id)sender {
return (action == #selector(sendByEmail:));
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void) sendByEmail: (id) sender {
// Some actions...
}
What I am doing wrong? Any help is appreciated. Thanks
In viewWillAppear or viewDidLoad , i added these
UIMenuItem *translateToMenu = [[UIMenuItem alloc] initWithTitle:#"Translate to.." action:#selector(translateTo:)];
UIMenuController *menuController = [UIMenuController sharedMenuController];
[menuController setMenuItems:[NSArray arrayWithObject:translateToMenu]];
[menuController setMenuVisible:YES animated:YES];
added this method
-(void) translateTo: (id) sender {}
and add only these 2 methods
- (BOOL) canPerformAction:(SEL)selector withSender:(id) sender {
if (selector == #selector(translateTo:))
return YES;
else
return NO;
}
- (BOOL) canBecomeFirstResponder {
return YES;
}
Try this and let me know…

Order of UIMenuItems in custom edit menu

I want to add my own commands to the selection menu, but also keep the standard "copy", "cut", etc. commands. I use this:
UIMenuItem *myItem = [[UIMenuItem alloc] initWithTitle:#"My Command" action:#selector(myCommand:)];
[[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObjects: myItem, nil]];
But this adds my command to the very end of the list in the edit menu. I want my command to appear first in it. How could I achieve this?
Solved it by myself. Here is what in my initWithCoder: method:
UIMenuItem *myCommandItem = [[UIMenuItem alloc] initWithTitle:#"My Command" action:#selector(myCommandPressed:)];
UIMenuItem *cutItem = [[UIMenuItem alloc] initWithTitle:#"Cut" action:#selector(myCut:)];
UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:#"Copy" action:#selector(myCopy:)];
UIMenuItem *pasteItem = [[UIMenuItem alloc] initWithTitle:#"Paste" action:#selector(myPaste:)];
UIMenuItem *selectItem = [[UIMenuItem alloc] initWithTitle:#"Select" action:#selector(mySelect:)];
UIMenuItem *selectAllItem = [[UIMenuItem alloc] initWithTitle:#"Select all" action:#selector(mySelectAll:)];
UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:#"Delete" action:#selector(myDelete:)];
[[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObjects: myCommandItem,
cutItem, copyItem, pasteItem, selectItem, selectAllItem, deleteItem, nil]];
Now this:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == #selector(myCommandPressed:)) {
return YES;
}
if (action == #selector(myCut:)) {
return [super canPerformAction:#selector(cut:) withSender:sender];
}
if (action == #selector(myCopy:)) {
return [super canPerformAction:#selector(copy:) withSender:sender];
}
if (action == #selector(myPaste:)) {
return [super canPerformAction:#selector(paste:) withSender:sender];
}
if (action == #selector(mySelect:)) {
return [super canPerformAction:#selector(select:) withSender:sender];
}
if (action == #selector(mySelectAll:)) {
return [super canPerformAction:#selector(selectAll:) withSender:sender];
}
if (action == #selector(myDelete:)) {
return [super canPerformAction:#selector(delete:) withSender:sender];
}
return NO;
}
And finally:
- (void) myCommandPressed: (id) sender {
NSLog(#"My Command pressed");
}
- (void) myCut: (id) sender {
[self cut:sender];
}
- (void) myCopy: (id) sender {
[self copy:sender];
}
- (void) myPaste: (id) sender {
[self paste:sender];
}
- (void) mySelect: (id) sender {
[self select:sender];
}
- (void) mySelectAll: (id) sender {
[self selectAll:sender];
}
- (void) myDelete: (id) sender {
[self delete:sender];
}

Resources