As my this question Display i want to display pop up when user select the text. and in that pop up detail about that word will be displayed.
But i didn't get any satisfactory answer so i have change my logic.
Now i want to display one item like Pop-Up in my UIMenuController and when user click that option than pop-up will displayed.
I have achieved this using this code,
UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:#"Pop-Up" action:#selector(displayPopUp:)];
[[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObject:menuItem]];
So my option is displaying and when i click that option than pop-up displays.But some other option is also display which i don't wanna, like this
I have googled it and get this code
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
[UIMenuController sharedMenuController].menuVisible = NO; //do not display the menu
if (action == #selector(copy:))
{
return NO;
}
else if (action == #selector(selectAll:))
{
return NO;
}
[self resignFirstResponder]; //do not allow the user to selected anything
return NO;
return [super canPerformAction:action withSender:sender];
}
But it didn't remove this extra item in UIMenuController.
The canPerformAction method is sent to everyone in the Responder chain. So, if the code you mention above is in the ViewController but the UITextView is the first Responder, it will not work. I found that the easiest thing to do was subclass UITextView and put the canPerformAction code in there. I disable all the default menuItems and create a menu of my own.
class rtfView: UITextView {
override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool {
if (action == #selector(textItem(_:))) || (action == #selector(h1Item(_:))) || (action == #selector(h2Item(_:))) || (action == #selector(h3Item(_:))) {
return true
} else {
return false
}
}
}
Related
I currently have an application which is for chatting.
I used a UItextField for input box and bubbles for display messages, some thing like the system SMS.
I want to enable copy paste on the message bubbles (labels). The problem is, when I want to show the UIMenuController, the label which i need to copy from need to become first responder. If the keyboard is currently displayed, when the label become first responder, the textfield will lost focus, thus the keyboard will be hide automatically.
this cause an UI scroll and feels not good.
Is there anyway that i can keep the keyboard shown even when i need to show the menu?
For those who still looking for answer here is code (main idea belongs to neon1, see linked question).
The idea is following: if a responder doesn't know how to handle given action, it propogates it to the next responder in chain. Until now we have two candidates for first responders:
Cell
TextField
Each of them have separate chain of responders (in fact, no, they do have common ancestor, so their chains have something in common, but we cannot use it):
UITextField <- UIView <- ... <- UIWindow <- UIApplication
UITableViewCell <- UIView <- ... <- UIWindow <- UIApplication
So we would like to have following chain of reponders:
UITextField <- UITableViewCell <- ..... <- UIWindow <- UIApplication
We need to subclass UITextField (code is taken from here):
CustomResponderTextView.h
#interface CustomResponderTextView : UITextView
#property (nonatomic, weak) UIResponder *overrideNextResponder;
#end
CustomResponderTextView.m
#implementation CustomResponderTextView
#synthesize overrideNextResponder;
- (UIResponder *)nextResponder {
if (overrideNextResponder != nil)
return overrideNextResponder;
else
return [super nextResponder];
}
#end
This code is very simple: it returns real responder in case we haven't set any custom next responder, otherwise returns our custom responder.
Now we can set new responder in our code (my example adds custom actions):
CustomCell.m
#implementation CustomCell
- (BOOL) canBecomeFirstResponder {
return YES;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
return (action == #selector(copyMessage:) || action == #selector(deleteMessage:));
}
#end
- (void) copyMessage:(id)sender {
// copy logic here
}
- (void) deleteMessage:(id)sender {
// delete logic here
}
Controller
- (void) viewDidLoad {
...
UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:#"Custom copy" action:#selector(copyMessage:)];
UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:#"Custom delete" action:#selector(deleteMessage:)];
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setMenuItems:#[copyItem, deleteItem]];
...
}
- (void) longCellTap {
// cell is UITableViewCell, that has received tap
if ([self.textField isFirstResponder]) {
self.messageTextView.overrideNextResponder = cell;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(menuDidHide:) name:UIMenuControllerDidHideMenuNotification object:nil];
} else {
[cell becomeFirstResponder];
}
}
- (void)menuDidHide:(NSNotification*)notification {
self.messageTextView.overrideNextResponder = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidHideMenuNotification object:nil];
}
Last step is making first responder (in our case text field) propogate copyMessage: and deleteMessage: actions to next responder (cell in our case). As we know iOs sends canPerformAction:withSender: to know, if given responder can handle the action.
We need to modify CustomResponderTextView.m and add the following function:
CustomResponderTextView.m
...
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (overrideNextResponder != nil)
return NO;
else
return [super canPerformAction:action withSender:sender];
}
...
In case we've set our custom next responder we send all actions to it (you can modify this part, if you need some actions on textField), otherwise we ask our supertype if it can handles it.
You can try to subclass your uitextfield and override the firstresponder. Check in your long press gesture handler if the uitextfield is the first responder and override the nextresponder.
Just did it in Swift via Nikita Took's solution.
I have a chat screen where there is a Text Field for text Input and Labels for messages (their display). When you tap on a message label, MENU (copy/paste/...) should appear, but the keyboard must stay open if already.
I subclassed the input text field:
import UIKit
class TxtInputField: UITextField {
weak var overrideNextResponder: UIResponder?
override func nextResponder() -> UIResponder? {
if overrideNextResponder != nil {
return overrideNextResponder
} else {
return super.nextResponder()
}
}
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if overrideNextResponder != nil {
return false
} else {
return super.canPerformAction(action, withSender: sender)
}
}
}
Then in my custom message label (subclass of UILabel but it can be a View Controller in your case) which has logic to start UIMenuController, I added after
if recognizer.state == UIGestureRecognizerState.Began { ...
the following chunk
if let activeTxtField = getMessageThreadInputSMSField() {
if activeTxtField.isFirstResponder() {
activeTxtField.overrideNextResponder = self
} else {
self.becomeFirstResponder()
}
} else {
self.becomeFirstResponder()
}
When user taps outside of UIMenuController
func willHideEditMenu() {
if let activeTxtField = getMessageThreadInputSMSField() {
activeTxtField.overrideNextResponder = nil
}
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIMenuControllerWillHideMenuNotification, object: nil)
}
You have to get the reference to the activeTxtField object. I did it iterating the Navigation stack, getting my View Controller which holds the desired text field and then using it.
Just in case you need it, here is the snippet for that part as well.
var activeTxtField = CutomTxtInputField()
for vc in navigationController?.viewControllers {
if vc is CustomMessageThreadVC {
let msgVC = vc as! CustomMessageThreadVC
activeTxtField = msgVC.textBubble
}
}
How do we disable Cut-Copy-Paste or Select-SelectAll menu when tapped on a UITextField. I tried with below code but it did not work.
if ([UIMenuController sharedMenuController]) {
[UIMenuController sharedMenuController].menuVisible = NO;
}
Make a subclass if UITextView and implement this function
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == #selector(paste:) || action == #selector(copy:))//and put other actions also
return NO;
return [super canPerformAction:action withSender:sender];
}
canBecomeFirstResponderhere should do the trick. make sure you delegate your UITextField
- (BOOL)canBecomeFirstResponder {
return NO;
}
i want to restrict the copy or Paste option for particular UITextfield in my Application.
Add the following piece of code in the implementation file of the view controller which containing the UITextField
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
UIMenuController *menuController = [UIMenuController sharedMenuController];
if (menuController) {
[UIMenuController sharedMenuController].menuVisible = NO;
}
return NO;
}
OR
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(paste:) // or #selector(copy:)
return NO;
return [super canPerformAction:action withSender:sender];
}
Create a subclass of UITextField.
In that subclass, implement
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (sel_isEqual(action, #selector(copy:))) //#selector(paste:) {
return NO;
}
return [super canPerformAction:action withSender:sender];
}
Then use this subclass for the field that you don't want to be able to copy in, and use a regular UITextField for the one that you can copy from.
Refer this URL for more info:-
iPhone – Disable the Cut/Copy/Paste Menu on UITextField
UIResponder
iPhone SDK Development
You can implement it like this:
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(paste:)) {
return NO;
}
return [super canPerformAction:action withSender:sender];
}
Otherwise you can write:
- (BOOL)canBecomeFirstResponder {
return NO;
}
To make your UITextField non-editable.
I'm trying to create a custom UIMenuController and display it in my view. Here's my code:
UIMenuController *menuController = [UIMenuController sharedMenuController];
UIMenuItem *listMenuItem = [[UIMenuItem alloc] initWithTitle:#"List" action:#selector(addList:)];
[menuController setMenuItems:[NSArray arrayWithObject:listMenuItem]];
[menuController setTargetRect:CGRectMake(50.0, 50.0, 0, 0) inView:self.view];
[menuController setMenuVisible:YES animated:YES];
[listMenuItem release];
There are no errors or exceptions, but the menu controller just doesn't show up.
You need to do three things:
You need to call -becomeFirstResponder on the view or view controller.
Your view or view controller needs to implement -canBecomeFirstResponder (returning YES).
Optionally, your view or view controller can implement -canPerformAction:action withSender:sender to show/hide menu items on an individual basis.
The answer mentions three things, but to be picky, there are six:
The menu handler must be a UIView. If it isn't, -becomeFirstResponder fails.
The menu handler must have userInteractionEnabled = YES
The menu handler must be in the view hierarchy and its -window property must be the same as the window for the view in the inView: argument.
You need to implement -canBecomeFirstResponder and return YES.
You need to call [handler becomeFirstResponder], before [menu setTargetRect:inView:] is called, or the latter will fail.
You need to call [menu setTargetRect:inView] (at least once) and [menu setMenuVisible:animated:].
In particular points 1-3 above got me. I wanted a custom menu handler class that was a UIResponder at first, which caused -becomeFirstResponder to return NO; then it was a UIView, which failed, then I tried making it a UIButton which worked, but only because userInteractionEnabled defaults to YES for buttons and NO for UIViews.
UIMenuController is visible on any view only if the view is first responder and
- (BOOL)canPerformAction method returns YES
Hence if your menu controller is to be shown on button click, the first line in the button action should be [self becomeFirstResponder]. NOTE: here self is the view which will present the menus.
If your menus are to be shown on long press gesture, then add longPressGesture to the UIView and in the longpress event before writing
[menuController setTargetRect:CGRectMake(50.0, 50.0, 0, 0) inView:self.view];
[menuController setMenuVisible:YES animated:YES];
write [self becomeFirstResponder];
Then follow the steps mentioned by OZ.
The below is a full commented working example ...
View subclass header file
#import <Foundation/Foundation.h>
#interface MenuControllerSupportingView : UIView
{
}
#end
View subclass source file
#import "MenuControllerSupportingView.h"
#implementation MenuControllerSupportingView
//It's mandatory and it has to return YES then only u can show menu items..
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)MenuItemAClicked
{
NSLog(#"Menu item A clicked");
}
-(void)MenuItemBClicked
{
NSLog(#"Menu item B clicked");
}
-(void)MenuItemCClicked
{
NSLog(#"Menu item C clicked");
}
//It's not mandatory for custom menu items
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if(action == #selector(MenuItemAClicked))
return YES;
else if(action == #selector(MenuItemBClicked))
return YES;
else if(action == #selector(MenuItemCClicked))
return YES;
else
return NO;
}
view Controller header file
#import <UIKit/UIKit.h>
#interface ViewController1 : UIViewController
#end
view Controller source file
#import "ViewController1.h"
#import "MenuControllerSupportingView.h"
#interface ViewController1 ()
{
MenuControllerSupportingView *vu;
}
#end
#implementation ViewController1
- (void)viewDidLoad
{
[super viewDidLoad];
vu=[[SGGI_MenuControllerSupportingView alloc]initWithFrame:CGRectMake(0,0,768,1024)];
[self.view addSubview:vu];
UIButton *btn=[UIButton buttonWithType:UIButtonTypeCustom];
[btn setFrame:CGRectMake(200,200,200,30)];
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btn setTitle:#"Show" forState:UIControlStateNormal];
[btn addTarget:self action:#selector(SHowMenu) forControlEvents:UIControlEventTouchUpInside];
[vu addSubview:btn];
}
-(void)SHowMenu
{
UIMenuController *menucontroller=[UIMenuController sharedMenuController];
UIMenuItem *MenuitemA=[[UIMenuItem alloc] initWithTitle:#"A" action:#selector(MenuItemAClicked)];
UIMenuItem *MenuitemB=[[UIMenuItem alloc] initWithTitle:#"B" action:#selector(MenuItemBClicked)];
UIMenuItem *MenuitemC=[[UIMenuItem alloc] initWithTitle:#"C" action:#selector(MenuItemCClicked)];
[menucontroller setMenuItems:[NSArray arrayWithObjects:MenuitemA,MenuitemB,MenuitemC,nil]];
//It's mandatory
[vu becomeFirstResponder];
//It's also mandatory ...remeber we've added a mehod on view class
if([vu canBecomeFirstResponder])
{
[menucontroller setTargetRect:CGRectMake(10,10, 0, 200) inView:vu];
[menucontroller setMenuVisible:YES animated:YES];
}
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
In View class if u write return YES alone in canPerformAction you will see all the default menuitems like camera symbol,cut,copy etc..
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return YES;
}
if u want to show something like camera alone then
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if(action==#selector(_insertImage:))
return YES;
else
return NO;
}
if u want to know about all the actions then
visit the link
Just in case anyone is having this issue specifically (and randomly) with iOS6: you might want to look at this SO related to having Speak Selection enabled on the device (Settings -> General -> Accessibility -> Speak Selection: On). A small number of my users were not able to see the custom UIMenuItems and this was the cause.
In Swift 3.0 -
In my case I wanted to have the VC pre-select the text in a TextView and display a custom menu for the user to take action on that selection. As mentioned by Kalle, order is very important, especially making setMenuVisible last.
In VC, viewDidLoad:
menuCont = UIMenuController.shared
let menuItem1: UIMenuItem = UIMenuItem(title: "Text", action: #selector(rtfView.textItem(_:)))
let menuItems: NSArray = [menuItem1]
menuCont.menuItems = menuItems as? [UIMenuItem]
In VC, when the user hits a button:
#IBAction func pressed(_ sender: Any) {
self.textView.selectedRange = NSMakeRange(rangeStart, rangeLength)
self.textView.becomeFirstResponder()
menuCont.setTargetRect(CGRect.zero, in: self.textView)
menuCont.setMenuVisible(true, animated: true)
}
Finally, in the sub-class of the TextView:
class rtfView: UITextView {
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any!) -> Bool {
if (action == #selector(textItem(_:))) {
return true
} else {
return false
}
}
}
maybe because CGRectMake(50.0, 50.0, 0, 0) creates a CGRect with width = 0 and height = 0?
cheers,
anka
I want to disable text selection on a UITextView. Until now what i've
already done is:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
[UIMenuController sharedMenuController].menuVisible = NO;
if (action == #selector(paste:))
return NO;
if (action == #selector(select:))
return NO;
if (action == #selector(selectAll:))
return NO;
return NO;
}
In this away I set UIMenuController to hidden and i put a stop to text copy but the text selection is still visible.
Google results (also StackOverflow) take me to no solution. Has someone already faced the
same problem? Any ideas?
If you want to prevent the text selection but keep links interactions, add the following textview delegate methods
- (void)textViewDidChangeSelection:(UITextView *)textView
{
[textView setSelectedRange:NSMakeRange(NSNotFound, 0)];
}
If you want to disable cut/copy/paste on all UITextView of your application you can use a category with :
#implementation UITextView (DisableCopyPaste)
- (BOOL)canBecomeFirstResponder
{
return NO;
}
#end
It saves a subclassing... :-)
Otherwise, just subclass UITextViewand put :
- (BOOL)canBecomeFirstResponder
{
return NO;
}
textView.editable = NO;
or
[textView setEnabled:NO];
im not sure what u meant