Como desativar Copiar, Cortar, Selecionar, Selecionar Tudo em UITextView

110

A funcionalidade de UITextViewCopiar, Cortar, Selecionar, Selecionar Tudo é exibida por padrão quando pressiono a tela. Mas, no meu projeto, o UITextFieldé somente leitura. Eu não preciso dessa funcionalidade. Por favor me diga como desabilitar este recurso.

Aishwarya
fonte
3
coloque [UIMenuController sharedMenuController] .menuVisible = NO; in - (BOOL) canPerformAction: (SEL) ação withSender: (id) método do remetente.
Ravoorinandan 01 de

Respostas:

108

A maneira mais fácil de desativar as operações da área de trabalho é criar uma subclasse de UITextViewque substitui o canPerformAction:withSender:método para retornar NOpara ações que você não deseja permitir:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(paste:))
        return NO;
    return [super canPerformAction:action withSender:sender];
}

Veja também UIResponder

Rpetrich
fonte
1
@rpetrichm, eu usei sua solução, copiar / colar / cortar / selecionar / selecionar todas as opções estão desabilitadas, mas ainda está chegando Substituir ... | BIU | Definir opções. Quero desativar esse menu completo.
Ameet Dhas
3
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; }- para bloquear todas as opções
Islam Q.
Isso é ótimo, no entanto, não sei como fazer isso para UISearchBar - como também há um campo de texto dentro, pensei que poderia sobrescrever o método de uma subclasse de UISearchBar, mas isso não funciona, infelizmente. Alguma ideia para isso?
borchero
eu tenho muitos controles. como permitir copiar e colar apenas em um controle?
Gaúcho
Isso não está funcionando para UITextView, embora eu tenha visto essa solução sugerida em vários lugares no SO. Alguém descobriu como fazer isso?
Pigpocket
67

Subclasse UITextView e substitua canBecomeFirstResponder:

- (BOOL)canBecomeFirstResponder {
    return NO;
}

Observe que isso se aplica apenas a UITextViews não editáveis! Não testei em editáveis ​​...

iCoder
fonte
Eu acho que return NO;no - (BOOL)canPerformAction:(SEL)action withSender:(id)sendermétodo é uma opção melhor.
Islam Q.
29

Se desejar desativar cortar / copiar / colar em todos os UITextView seus aplicativos, você pode usar uma categoria com:

@implementation UITextView (DisableCopyPaste)

- (BOOL)canBecomeFirstResponder
{
    return NO;
}

@end

Ele salva uma subclasse ... :-)

Damien Debin
fonte
3
você também pode colocar isso apenas em seu arquivo /, onde você precisa desse comportamento.
markus_p
4
Isso só funciona como um efeito colateral e evita que o UITextViewcomporte-se conforme o esperado quando, por exemplo, é editável e recebe um toque. É muito para substituir canPerformAction:withSender:; é para isso que serve o protocolo.
jdc
16
Você não pode substituir com segurança um método usando uma categoria. Este é um comportamento indefinido. Você deve subclassificar para substituir um método com segurança.
Rob Napier,
2
Nota: Isso se aplica a todos os UITextViews em seu aplicativo. Não é ideal na maioria das vezes.
bbrame de
2
Observe também que a Apple desaconselha isso : "Se o nome de um método declarado em uma categoria for o mesmo que um método na classe original, ou um método em outra categoria na mesma classe (ou mesmo uma superclasse), o comportamento é indefinido sobre qual método de implementação é usado em tempo de execução ... [e] pode causar problemas ao usar categorias para adicionar métodos às classes padrão Cocoa ou Cocoa Touch. "
Rob
29

Esta foi a melhor solução de trabalho para mim:

UIView *overlay = [[UIView alloc] init];  
[overlay setFrame:CGRectMake(0, 0, myTextView.contentSize.width, myTextView.contentSize.height)];  
[myTextView addSubview:overlay];  
[overlay release];

de: https://stackoverflow.com/a/5704584/1293949

Saraiva Alcides
fonte
3
bom, sem subclasses aqui!
Martin Reichl
7
Eu amo a abordagem de fita adesiva e arame de enfardar aqui. Queria poder marcar você com +1 novamente :) Em vez disso, aqui está uma estrela dourada: i.imgur.com/EXLFt1Z.jpg
jdc
22

A resposta @rpetrich funcionou para mim. Estou postando o código expandido, caso isso economize algum tempo.

No meu caso, não quero nenhum pop-up, mas quero que o UITextField seja capaz de se tornar o primeiro a responder.

Infelizmente, você ainda obtém o pop-up da lupa quando toca e segura o campo de texto.

@interface NoSelectTextField : UITextField

@end

@implementation NoSelectTextField

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    if (action == @selector(paste:) ||
        action == @selector(cut:) ||
        action == @selector(copy:) ||
        action == @selector(select:) ||
        action == @selector(selectAll:) ||
        action == @selector(delete:) ||
        action == @selector(makeTextWritingDirectionLeftToRight:) ||
        action == @selector(makeTextWritingDirectionRightToLeft:) ||
        action == @selector(toggleBoldface:) ||
        action == @selector(toggleItalics:) ||
        action == @selector(toggleUnderline:)
        ) {
            return NO;
    }
    return [super canPerformAction:action withSender:sender];
}

@end

Swift 4

class NoSelectTextField: UITextField {

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(paste(_:)) ||
            action == #selector(cut(_:)) ||
            action == #selector(copy(_:)) ||
            action == #selector(select(_:)) ||
            action == #selector(selectAll(_:)) ||
            action == #selector(delete(_:)) ||
            action == #selector(makeTextWritingDirectionLeftToRight(_:)) ||
            action == #selector(makeTextWritingDirectionRightToLeft(_:)) ||
            action == #selector(toggleBoldface(_:)) ||
            action == #selector(toggleItalics(_:)) ||
            action == #selector(toggleUnderline(_:)) {
            return false
        }
        return super.canPerformAction(action, withSender: sender)
    }

}
Devin Pitcher
fonte
1
Por favor, poste uma versão rápida para isso também. obrigado.
Salman Khakwani
@pinch: tnx. Ainda segura a água no iOS 12.1.3.
YvesLeBorg
20

Se você não precisa de UITextView para rolar, a solução mais simples que não envolve subclassificação é simplesmente desativar a interação do usuário para a visualização de texto:

textField.userInteractionEnabled = NO;
Luke Redpath
fonte
2
Isso elimina o toque em links, etc. se for o que está na visualização de texto. Deve-se notar que esta não é uma boa solução para querer ocultar o selecionar / copiar / colar, mas também manter algum nível de interação habilitado.
barfoon
3
Bem, eu teria pensado que isso seria óbvio.
Luke Redpath
Estou usando campos de texto como rótulos para imagens em um jogo e não quero que a lupa apareça e não há razão para copiar o texto. Isso funciona muito bem para mim. Estou usando texto estilizado em um UIWebView e a mesma linha também funciona lá.
JScarry
15

A maneira mais fácil é criar uma subclasse de UITextView que substitui canPerformAction: withSender:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender    
{    
     [UIMenuController sharedMenuController].menuVisible = NO;  //do not display the menu
     [self resignFirstResponder];                      //do not allow the user to selected anything
     return NO;
}
haiLong
fonte
Esta é a melhor solução para poder digitar texto, mas nada mais, e permite que toques no campo de texto não sejam mais "interceptados". Isso faz o que o OP queria e muito mais.
hlfcoding
13

Quando eu retornar NÃO no canPerformAction no iOS 7, obtenho muitos erros como este:

<Error>: CGContextSetFillColorWithColor: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.

Minha solução é a seguinte:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
    }];
    return [super canPerformAction:action withSender:sender];
}

O truque é ocultar o controlador de menu no próximo ciclo na fila principal (logo depois de ser exibido).

Adam Wallner
fonte
muito bom. o único problema é que tenho 2 textviews e um textField e quero evitar o copiar e colar apenas nos campos textView. como identificar quem chamou o canPerformAction? a variável do remetente é o UIMenuController
Gaúcho
1
Ele funciona na subclasse de UITextField (ou UITextView). Se você não usar a subclasse que criou, ela não terá nenhum efeito. Por exemplo, eu criei um TextFieldWithoutCopyPaste e usei onde não queria ter a funcionalidade de copiar e colar.
Adam Wallner
bem, você está certo. Mas, no meu caso, o textView precisa da subclasse para usar canPerformAction e o textField precisa da subclasse para usar o textFieldDidBeginEditing a fim de animar a janela quando o teclado é mostrado. a animação move a janela com o teclado. Eu uso esse método para evitar que o teclado cubra o textField.
Gaúcho
10

Esta é a maneira mais fácil de desativar todo o menu Selecionar / Copiar / Colar em um UITextView

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{    
    [UIMenuController sharedMenuController].menuVisible = NO;
    return NO;    
}
GL777
fonte
Funciona para UITextField também. Obrigado por compartilhar.
Gaurav Srivastava
1
Estou usando o código acima e ainda estou recebendo o menu como desativar isso, por favor me ajude.
Bittoo
4

Desde o iOS 7, há uma propriedade no UITextView:

 @property(nonatomic,getter=isSelectable) BOOL selectable;

Isso impede que uma visualização permita seleções de texto. Funciona muito bem para mim.

Epaga
fonte
4

Se você está procurando substituir o teclado por um, digamos, UIPickercomo inputView(com uma barra de ferramentas como um inputAccesotyView), então esta solução alternativa pode ajudar ...

  • Implemento textFieldShouldBeginEditing:
  • colocar dentro textField.userInteractionEnabled = NO;
  • Em seguida, quando estiver prestes a fechar o UIPickerView, defina-o como SIM.

Ao fazer isso, você seria capaz de tocar em UITextFielde mostrar as opções para escolher UIPickerView, neste momento você UITextField, de fato, não reagiria a qualquer evento de toque (isso inclui tocar e segurar para cortar, copiar e colar). No entanto, você deve se lembrar de defini-lo de volta para SIM quando estiver fechando o seu, UIPickerViewmas não será possível acessar o UIPickerViewnovamente.

O único momento em que falha é quando o usuário começa tocando e segurando o botão UITextView, então você verá cortar, copiar e colar novamente pela primeira vez. É por isso que você deve sempre validar suas entradas. É o mais fácil que consigo pensar. A outra opção era usar um UILabelpara texto somente leitura, mas você perde muitas funcionalidades do UITextView.

rn3sto
fonte
4

Subclasse UITextView - swift 4.0

     override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return false
    }
Manee ios
fonte
Que estranho? ainda mostra a opção "Colar", mas não o faça se pressionado ...
Fluxo do iOS de
4

Se você deseja desativar o pop-up para UITextField, tente este UITextFieldDelegatemétodo para alternar isUserInteractionEnabled.

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    textField.isUserInteractionEnabled = false
    return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
    textField.isUserInteractionEnabled = true
    return true
}
Jayesh Thanki
fonte
3

Isso pode ser feito facilmente no storyboard (Xcode 6). Apenas desmarque Editable e Selectable no Attributes Inspector. Você ainda pode rolar a tela de texto.insira a descrição da imagem aqui

Hari Kunwar
fonte
3

Isso funcionou para mim. Certifique-se de chamar resignFirstResponder no textView

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
  [self.textView resignFirstResponder];
  return NO;
}
verma
fonte
2

Eu forneci uma resposta funcional aqui para desativar a seleção de texto + lupa, mantendo links clicáveis ​​habilitados Espero que ajude:

Depois de muito tempo tentando, consegui interromper a seleção de texto, ampliação e detecção de dados (links clicáveis ​​etc) substituindo addGestureRecognizer em uma subclasse UITextView permitindo apenas UILongPressGestureRecognizer atrasar o término do toque:

UIUnselectableTextView.m

-(void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
    if([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] && gestureRecognizer.delaysTouchesEnded)
    {
        [super addGestureRecognizer:gestureRecognizer];
    }
}
Thibaud David
fonte
2

Para Swift 3, ele foi alterado para:

override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return false
}
Andrey Gordeev
fonte
2

Você pode corrigir isso em seu storyboard desmarcando estas caixas:

insira a descrição da imagem aqui

Ou você pode definir programaticamente assim:

textView.selectable = false
textView.editable = false
Khuong
fonte
1

Eu fiz isso No meuUITextView , desativei a opção cortar, copiar, selecionar, etc. com muita facilidade.

Coloquei um UIViewno mesmo lugar onde havia colocado o UITextView, mas em self.viewe adicionei um touchDelegatemétodo como segue:

(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
    UITouch *scrollTouch=[touches anyObject];
    if(scrollTouch.view.tag==1)
    {
        NSLog(@"viewTouched");
        if(scrollTouch.tapCount==1)
            [textView1 becomeFirstResponder];
        else if(scrollTouch.tapCount==2)
        {
            NSLog(@"double touch");
            return;
        }

    }
}

e funcionou para mim. Obrigado.

Bill the Lizard
fonte
1

Rápido

textView.selectable = false // disable text selection (and thus copy/paste/etc)

Relacionados

textView.editable = false // text cannot be changed but can still be selected and copied
textView.userInteractionEnabled = false // disables all interaction, including scrolling, clicking on links, etc.
Suragch
fonte
1

Se você deseja adicionar uma opção personalizada ao seu UITextView, mas desabilitar as funções existentes, é assim que você faz no Swift 3 :

Para desativar a funcionalidade de copiar, colar e cortar, crie uma subclasse e substitua o seguinte:

override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return false
} 

No ViewController você tem seu CustomTextView e adiciona o seguinte para adicionar suas opções:

  let selectText = UIMenuItem(title: "Select", action: #selector(ViewController.selected))

    func selected() {

    if let selectedRange = textView.selectedTextRange, let 
     selectedText = textView.text(in: selectedRange) {

     }


    print("User selected text: \(selectedText)")

    }
Sam Bing
fonte
1

Este método desabilitará completamente o menu Selecionar, Selecionar Tudo, Colar. Se você ainda conseguir alguma outra ação, basta adicioná-la à condição if como abaixo.

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender // This is to disable select / copy / select all / paste menu
    {
        if (action == @selector(copy:) || action == @selector(selectAll:) || action == @selector(select:) || action == @selector(paste:))
            return NO;
        return [super canPerformAction:action withSender:sender];
    }
Sivrish Thangamani
fonte
0

Você pode apenas criar uma categoria como esta:

UITextView + Selectable.h

@interface UITextView (Selectable)

@property (nonatomic, assign, getter = isTextSelectable) bool textSelectable;

@end

UITextView + Selectable.m

#import "UITextView+Selectable.h"

#import <objc/runtime.h>

#define TEXT_SELECTABLE_PROPERTY_KEY @"textSelectablePropertyKey"

@implementation UITextView (Selectable)

@dynamic textSelectable;

-(void)setTextSelectable:(bool)textSelectable {
    objc_setAssociatedObject(self, TEXT_SELECTABLE_PROPERTY_KEY, [NSNumber numberWithBool:textSelectable], OBJC_ASSOCIATION_ASSIGN);
}

-(bool)isTextSelectable {
    return [objc_getAssociatedObject(self, TEXT_SELECTABLE_PROPERTY_KEY) boolValue];
}

-(bool)canBecomeFirstResponder {
    return [self isTextSelectable];
}

@end
user2091319
fonte
1
Esta não é uma boa maneira de resolver o problema. Em primeiro lugar, isso afeta tudo, pois está na categoria. Em segundo lugar, usar objetos associados para uma tarefa bastante fácil pode causar mais problemas posteriormente com a depuração (por que funciona assim quando você esquece o que fez) do que dar lucro. É bom evitá-lo sempre que possível, na minha opinião. Torna o código menos fácil de localizar, depurar e entender por um novo programador no projeto.
Vive de
0

Subclassificação UITextViewe substituição- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer é outra possibilidade de desabilitar ações indesejadas.

Use a classe do gestureRecognizerobjeto -para decidir se a ação deve ser adicionada ou não.

RhodanV5500
fonte
0

(SWIFT) Se você quiser apenas um campo de texto básico sem nenhuma das opções de menu ou lupa, crie uma subclasse de UITextField retornando false para gestoRecognizerShouldBegin:

class TextFieldBasic: UITextField {
    override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {

        return false
    }
}

Isso irá ignorar toda a funcionalidade de toque no campo de texto, mas ainda permitirá que você use o teclado pop-up para adicionar / remover caracteres.

Se você estiver usando o storyboard, apenas atribua a classe recém-criada ao campo de texto ou se estiver criando um campo de texto programaticamente:

var basicTextField = TextFieldBasic()
basic = basicTextField(frame: CGRectMake(10, 100, 100,35))
basic.backgroundColor = UIColor.redColor()
self.view.addSubview(basic)

basic.becomeFirstResponder()
Krivvenz
fonte
0
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool 
{
    NSOperationQueue .mainQueue().addOperationWithBlock({ () -> Void in   

        [UIMenuController .sharedMenuController() .setMenuVisible(false, animated: true)]

    })
    return super.canPerformAction(action, withSender: sender)}
Nirav Ghori
fonte
0

Swift 3

Para fazer isso, você precisa criar uma subclasse de seu UITextView e colocar este método.

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if (action == #selector(copy(_:))) {
            return false
        }

        if (action == #selector(cut(_:))) {
            return false
        }

        if (action == #selector(paste(_:))) {
            return false
        }

        return super.canPerformAction(action, withSender: sender)
    }
Zaldy
fonte
0

UITextView tem duas propriedades que farão o que você precisa: isSelectable e isEditable .

Definindo isEditable como false, você evitará que o usuário edite o texto e definindo isSelectable como false, você evitará que o usuário selecione o texto dentro de seu textView, então isso impedirá que o menu de ação seja mostrado.

L. Davì
fonte
0

Encontre o código de amostra para referência:

 override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(copy(_:)) || action == #selector(paste(_:)) || action == #selector(UIResponderStandardEditActions.paste(_:)) ||
            action == #selector(replace(_:withText:)) ||
            action == #selector(UIResponderStandardEditActions.cut(_:)) ||
        action == #selector(UIResponderStandardEditActions.select(_:)) ||
        action == #selector(UIResponderStandardEditActions.selectAll(_:)) ||
        action == #selector(UIResponderStandardEditActions.delete(_:)) ||
        action == #selector(UIResponderStandardEditActions.makeTextWritingDirectionLeftToRight(_:)) ||
        action == #selector(UIResponderStandardEditActions.makeTextWritingDirectionRightToLeft(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleBoldface(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleItalics(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleUnderline(_:)) ||
        action == #selector(UIResponderStandardEditActions.increaseSize(_:)) ||
        action == #selector(UIResponderStandardEditActions.decreaseSize(_:))

       {
            return false
        }

        return true
    }
Sameen Ahmad
fonte
-1

Use func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { retrun bool }no lugar de textFieldShouldBeginEditing.

class ViewController: UIViewController , UITextFieldDelegate {

    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        //Show date picker
        let datePicker = UIDatePicker()
        datePicker.datePickerMode = UIDatePickerMode.date
        textField.tag = 1
        textField.inputView = datePicker
    }

    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        if textField.tag == 1 {
            textField.text = ""
            return false
        }

        return true
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if textField.tag == 1 {
            textField.text = ""
            return false
        }

        return true
    }
}

Crie uma nova classe com o nome StopPasteAction.swift

import UIKit

class StopPasteAction: UITextField {

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return false
    }
}

Adicione a classe nova classe com seu TextField atual

insira a descrição da imagem aqui

anson
fonte