Cómo deshabilitar Copiar, Cortar, Seleccionar, Seleccionar Todo en UITextView

110

La funcionalidad UITextView'Copiar, Cortar, Seleccionar, Seleccionar Todo' se muestra de forma predeterminada cuando presiono hacia abajo en la pantalla. Pero, en mi proyecto, UITextFieldsolo es de lectura. No necesito esta funcionalidad. Por favor dígame cómo deshabilitar esta función.

Aishwarya
fuente
3
lugar [UIMenuController sharedMenuController] .menuVisible = NO; in - (BOOL) canPerformAction: (SEL) action withSender: (id) método de remitente.
ravoorinandan

Respuestas:

108

La forma más sencilla de deshabilitar las operaciones de la mesa de trabajo es crear una subclase UITextViewque anule el canPerformAction:withSender:método para devolver las NOacciones que no desea permitir:

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

Ver también UIResponder

rpetrich
fuente
1
@rpetrichm, usé su solución, copiar / pegar / cortar / seleccionar / seleccionar Todas las opciones están deshabilitadas pero aún están por venir Reemplazar ... | BIU | Definir opciones. Quiero desactivar ese menú completo.
Ameet Dhas
3
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; }- para bloquear todas las opciones
Islam Q.10 de
Eso es genial, sin embargo, no sé cómo hacer esto para UISearchBar, ya que también hay un campo de texto dentro, pensé que podría sobrescribir el método de una subclase de UISearchBar, pero eso no funciona desafortunadamente. ¿Alguna idea para esto?
borchero
tengo muchos controles. ¿Cómo permitir copiar y pegar solo en un control?
Gaucho
Esto no funciona para UITextView, aunque he visto esta solución sugerida en varios lugares en SO. ¿Alguien ha descubierto cómo hacer esto?
Pigpocket
67

Subclase UITextView y sobrescribir canBecomeFirstResponder:

- (BOOL)canBecomeFirstResponder {
    return NO;
}

Tenga en cuenta que esto solo se aplica a UITextViews no editables. No lo he probado en editables ...

iCoder
fuente
Creo que return NO;el - (BOOL)canPerformAction:(SEL)action withSender:(id)sendermétodo es una mejor opción.
Islam Q.10 de
29

Si desea deshabilitar cortar / copiar / pegar en toda UITextView su aplicación, puede usar una categoría con:

@implementation UITextView (DisableCopyPaste)

- (BOOL)canBecomeFirstResponder
{
    return NO;
}

@end

Guarda una subclasificación ... :-)

Damien Debin
fuente
3
también puede poner esto solo en su archivo /, donde necesite este comportamiento.
markus_p
4
Esto solo funciona como un efecto secundario y evita que se UITextViewcomporte como se esperaba cuando, por ejemplo, es editable y recibe un toque. Es mucho para anular canPerformAction:withSender:; para eso es el protocolo.
jdc
16
No puede anular un método de forma segura mediante una categoría. Este es un comportamiento indefinido. Debe crear una subclase para invalidar un método de forma segura.
Rob Napier
2
Nota: Esto se aplicará a todos los UITextViews en su aplicación. No es ideal la mayor parte del tiempo.
bbrame
2
También tenga en cuenta que Apple desaconseja esto : "Si el nombre de un método declarado en una categoría es el mismo que un método en la clase original, o un método en otra categoría en la misma clase (o incluso una superclase), el comportamiento es indefinido en cuanto a qué implementación de método se usa en tiempo de ejecución ... [y] puede causar problemas al usar categorías para agregar métodos a las clases estándar de Cocoa o Cocoa Touch ".
Rob
29

Esta fue la mejor solución de trabajo para mí:

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
fuente
3
bonito, no hay subclases aquí!
Martin Reichl
7
Me encanta el enfoque de la cinta adhesiva y el alambre para empacar aquí. Me gustaría poder hacer +1 en ti otra vez :) En cambio, aquí hay una estrella dorada: i.imgur.com/EXLFt1Z.jpg
jdc
22

La respuesta de @rpetrich funcionó para mí. Estoy publicando el código expandido en caso de que le ahorre a alguien algo de tiempo.

En mi caso, no quiero ninguna ventana emergente, pero sí quiero que UITextField pueda convertirse en el primer respondedor.

Desafortunadamente, todavía aparece la ventana emergente de la lupa cuando mantiene presionado el 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

Rápido 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)
    }

}
Jarra Devin
fuente
1
Por favor publique también una versión rápida para esto. gracias.
Salman Khakwani
@pinch: tnx. Todavía aguanta el agua en iOS 12.1.3.
YvesLeBorg
20

Si no necesita UITextView para desplazarse, entonces la solución más simple que no implica subclases es simplemente deshabilitar la interacción del usuario para la vista de texto:

textField.userInteractionEnabled = NO;
Luke Redpath
fuente
2
Esto quita tocar los enlaces, etc. si eso es lo que está en la vista de texto. Debe tenerse en cuenta que esta no es una buena solución para querer ocultar la selección / copiar / pegar, pero también mantener habilitado cierto nivel de interacción.
barfoon
3
Bueno, habría pensado que sería obvio.
Luke Redpath
Estoy usando campos de texto como etiquetas para imágenes en un juego y no quiero que aparezca la lupa y no hay razón para copiar el texto. Esto funciona muy bien para mí. Estoy usando texto con estilo en un UIWebView y la misma línea también funciona allí.
JScarry
15

La forma más sencilla es crear una subclase de UITextView que anule 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
fuente
Esta es la mejor solución para poder escribir texto, pero nada más, y permite que los toques en el campo de texto ya no se "intercepten". Esto hace lo que quería el OP, y más.
hlfcoding
13

Cuando devuelva NO en canPerformAction en iOS 7, obtendré muchos errores 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.

Mi solución es la siguiente:

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

El truco consiste en ocultar el controlador del menú en el siguiente ciclo de la cola principal (justo después de que se muestre).

Adam Wallner
fuente
muy agradable. el único problema es que tengo 2 vistas de texto y un campo de texto y quiero evitar copiar y pegar solo en los campos de vista de texto. ¿Cómo identificar quién llamó a canPerformAction? la variable remitente es el UIMenuController
Gaucho
1
Funciona en la subclase de UITextField (o UITextView). Si no usa la subclase que creó, no tendrá ningún efecto. Por ejemplo, creé un TextFieldWithoutCopyPaste y lo usé donde no quería tener la funcionalidad de copiar y pegar.
Adam Wallner
ok, tienes razón. Pero en mi caso, textView necesita una subclase para usar canPerformAction y textField necesita una subclase para usar textFieldDidBeginEditing para animar la ventana cuando se muestra el teclado. la animación mueve la ventana con el teclado. Utilizo este método para evitar que el teclado cubra el campo de texto.
Gaucho
10

Esta es la forma más fácil de deshabilitar todo el menú Seleccionar / Copiar / Pegar en un UITextView

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{    
    [UIMenuController sharedMenuController].menuVisible = NO;
    return NO;    
}
GL777
fuente
También funciona para UITextField. Gracias por compartir.
Gaurav Srivastava
1
Estoy usando el código anterior todavía estoy obteniendo el menú de cómo deshabilitarlo, por favor ayúdenme.
Bittoo
4

Desde iOS 7 hay una propiedad en UITextView:

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

Esto evita que una vista permita selecciones de texto. Funciona muy bien para mi.

Epaga
fuente
4

Si está buscando reemplazar el teclado con, digamos, UIPickercomo inputView(con, por supuesto, una barra de herramientas como inputAccesotyView), entonces esta solución podría ayudar ...

  • Implementar textFieldShouldBeginEditing:
  • adentro poner textField.userInteractionEnabled = NO;
  • Luego, cuando esté a punto de cerrar UIPickerView, ajústelo en SÍ.

Al hacer esto, podría tocar UITextFieldy mostrar las opciones para elegir UIPickerView, en este momento UITextField, de hecho, no reaccionaría a ningún evento táctil (esto incluye tocar y mantener presionado para cortar, copiar y pegar). Sin embargo, debe recordar volver a configurarlo en SÍ cuando cierre su, UIPickerViewsin embargo, no podrá volver a acceder UIPickerViewa él.

El único momento en que falla es cuando el usuario comienza presionando y manteniendo presionado UITextView, luego verá cortar, copiar y pegar nuevamente por primera vez. Es por eso que siempre debe validar sus entradas. Esto es lo más fácil que se me ocurre. La otra opción era usar un UILabelpara texto de solo lectura, pero se pierden muchas funciones excelentes de UITextView.

rn3sto
fuente
4

Subclase UITextView - swift 4.0

     override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return false
    }
Manee ios
fuente
¿Que raro? todavía muestra la opción "Pegar", pero no lo haga si se presiona ...
iOS Flow
4

Si desea desactivar la ventana emergente UITextField, pruebe 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
fuente
3

Esto se puede hacer fácilmente en el guión gráfico (Xcode 6). Simplemente desmarque Editable y Seleccionable en Attributes Inspector. Aún puede desplazarse por la vista de texto.ingrese la descripción de la imagen aquí

Hari Kunwar
fuente
3

Esto funcionó para mí. Asegúrese de llamar a resignFirstResponder en textView

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

Proporcioné una respuesta funcional aquí para deshabilitar la selección de texto + lupa, manteniendo habilitados los enlaces clikable Espero que ayude:

Después de intentarlo durante bastante tiempo, logré detener la selección de texto, la ampliación y el mantenimiento de la detección de datos (enlaces en los que se puede hacer clic, etc.) anulando addGestureRecognizer en una subclase UITextView que solo permite que UILongPressGestureRecognizer retrase el final táctil:

UIUnselectableTextView.m

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

Para Swift 3 se cambió a:

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

Puede solucionar esto en su guión gráfico desmarcando estas casillas:

ingrese la descripción de la imagen aquí

O puede configurar programáticamente así:

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

Lo he hecho. En mi UITextViewhe desactivado la opción cortar, copiar, seleccionar, etc. muy fácilmente.

Coloqué un UIViewen el mismo lugar donde había colocado el UITextView, pero self.viewy agregué un touchDelegatemétodo de la siguiente manera:

(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;
        }

    }
}

Y funcionó para mí. Gracias.

Bill el lagarto
fuente
1

Rápido

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

Relacionado

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
fuente
1

Si desea agregar una opción personalizada a su UITextView pero deshabilitar las funciones existentes, así es como lo hace en Swift 3 :

Para deshabilitar la funcionalidad de copiar, pegar, cortar, cree una subclase y anule lo siguiente:

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

En ViewController tienes tu CustomTextView agrega lo siguiente para agregar tus opciones:

  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
fuente
1

Este método deshabilitará completamente el menú Seleccionar, Seleccionar todo, Pegar. Si aún obtiene alguna otra acción, simplemente agréguela a la condición if como se muestra a continuación.

- (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
fuente
0

Puedes crear una categoría como esta:

UITextView + Seleccionable.h

@interface UITextView (Selectable)

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

@end

UITextView + Seleccionable.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
usuario2091319
fuente
1
Esta no es una buena forma de solucionarlo. En primer lugar, afecta a todo, ya que está en la categoría. En segundo lugar, el uso de objetos asociados para una tarea bastante sencilla puede generar más problemas más adelante con la depuración (por qué funciona así cuando olvida lo que hizo) que generar ganancias. En mi opinión, es bueno evitarlo siempre que sea posible. Hace que el código sea menos fácil de encontrar, depurar y comprender por parte del nuevo programador del proyecto.
Vive el
0

Subclasificar UITextViewy reemplazar - (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizeres otra posibilidad para deshabilitar acciones no deseadas.

Utilice la clase del gestureRecognizer-objeto para decidir si la acción debe agregarse o no.

RhodanV5500
fuente
0

(SWIFT) Si solo desea un campo de texto básico sin ninguna de las opciones de menú o lupa, cree una subclase de UITextField que devuelva falso a gestoRecognizerShouldBegin:

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

        return false
    }
}

Esto omitirá toda la funcionalidad táctil en el campo de texto, pero aún le permitirá usar el teclado emergente para agregar / eliminar caracteres.

Si está utilizando el guión gráfico, simplemente asigne la clase recién creada al campo de texto o si está creando un campo de texto mediante programación:

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

basic.becomeFirstResponder()
Krivvenz
fuente
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
fuente
0

Swift 3

Para hacer esto, necesita subclasificar su UITextView y poner 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
fuente
0

UITextView tiene dos propiedades que harán lo que necesite: isSelectable e isEditable .

Si establece isEditable en false, evitará que el usuario edite el texto y si establece isSelectable en false, evitará que el usuario seleccione texto dentro de su textView, por lo que evitará que se muestre el menú de acciones.

L. Davì
fuente
0

Encuentre el código de muestra como referencia:

 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
fuente
-1

Utilizar func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { retrun bool }en 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
    }
}

Cree una nueva clase con el nombre StopPasteAction.swift

import UIKit

class StopPasteAction: UITextField {

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

Agregue la clase nueva clase con su TextField actual

ingrese la descripción de la imagen aquí

anson
fuente