Как отключить копирование, вырезание, выделение, выделение всего в UITextView


110

Функции UITextViewКопировать, Вырезать, Выбрать, Выбрать все отображаются по умолчанию, когда я нажимаю на экран. Но в моем проекте UITextFieldэто только для чтения. Мне эта функция не нужна. Подскажите пожалуйста, как отключить эту функцию.


3
поместите [UIMenuController sharedMenuController] .menuVisible = NO; in - (BOOL) canPerformAction: (SEL) действие с методом отправителяSender: (id).
ravoorinandan 01

Ответы:


108

Самый простой способ отключить операции монтажного стола - создать подкласс, UITextViewкоторый переопределяет canPerformAction:withSender:метод, возвращаемый NOдля действий, которые вы не хотите разрешать:

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

Также см. UIResponder


1
@rpetrichm, я использовал ваше решение, скопировать / вставить / вырезать / выбрать / выбрать Все параметры отключены, но все еще идет Заменить ... | BIU | Определить параметры. Я хочу отключить это полное меню.
Амит Дхас

3
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; }- чтобы заблокировать все варианты
Islam Q.

Это здорово, однако я не знаю, как это сделать для UISearchBar - поскольку внутри также есть текстовое поле, я думал, что могу перезаписать метод подкласса UISearchBar, но это, к сожалению, не работает. Есть идеи для этого?
borchero

У меня много элементов управления. как разрешить копирование и вставку только на одном элементе управления?
Гаучо

Это не работает для UITextView, хотя я видел это решение, предложенное в нескольких местах на SO. Кто-нибудь придумал, как это сделать?
Pigpocket

67

Подкласс UITextView и перезапись canBecomeFirstResponder:

- (BOOL)canBecomeFirstResponder {
    return NO;
}

Обратите внимание, что это применимо только к нередактируемым UITextViews! Не тестировал на редактируемых ...


Я думаю , что return NO;по - (BOOL)canPerformAction:(SEL)action withSender:(id)senderметоду является лучшим вариантом.
Islam Q.

29

Если вы хотите отключить вырезание / копирование / вставку во всем UITextView своем приложении, вы можете использовать категорию с:

@implementation UITextView (DisableCopyPaste)

- (BOOL)canBecomeFirstResponder
{
    return NO;
}

@end

Сохраняет подкласс ... :-)


3
вы также можете поместить это только в свой /, файл, где вам нужно это поведение.
markus_p 03

4
Это работает только как побочный эффект и не позволяет UITextViewобъекту вести себя должным образом, когда, например, он доступен для редактирования и получает прикосновение. Это многое, что нужно преодолеть canPerformAction:withSender:; вот для чего нужен протокол.
jdc 06

16
Вы не можете безопасно переопределить метод с помощью категории. Это неопределенное поведение. Вы должны создать подкласс, чтобы безопасно переопределить метод.
Роб Напье

2
Примечание. Это будет применяться ко всем UITextView в вашем приложении. Не идеально в большинстве случаев.
bbrame

2
Также обратите внимание, что Apple не рекомендует этого : «Если имя метода, объявленного в категории, совпадает с именем метода в исходном классе или метода в другой категории того же класса (или даже суперкласса), поведение будет таким же. undefined относительно того, какая реализация метода используется во время выполнения ... [и] может вызвать проблемы при использовании категорий для добавления методов к стандартным классам Cocoa или Cocoa Touch ».
Роб

29

Для меня это было лучшее рабочее решение:

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

из: https://stackoverflow.com/a/5704584/1293949


3
хороший, здесь нет подкласса!
Мартин Райхль

7
Мне здесь нравится использование клейкой ленты и проволоки. Хотел бы я снова поставить
jdc 06

22

Ответ @rpetrich сработал для меня. Я публикую расширенный код на случай, если он кому-то сэкономит время.

В моем случае я не хочу вообще никакого всплывающего окна, но я хочу, чтобы UITextField мог стать первым респондентом.

К сожалению, при нажатии и удерживании текстового поля появляется всплывающее окно с лупой.

@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)
    }

}

1
Пожалуйста, опубликуйте быструю версию и для этого. Спасибо.
Салман Хаквани

@pinch: TNX. В iOS 12.1.3 все еще держится вода.
YvesLeBorg 01

20

Если для прокрутки UITextView не требуется, то самое простое решение, не использующее подклассы, - просто отключить взаимодействие с пользователем для текстового представления:

textField.userInteractionEnabled = NO;

2
Это убирает нажатие на ссылки и т.д., если это то, что есть в текстовом окне. Следует отметить, что это не лучшее решение, если вы хотите скрыть выбор / копирование / вставку, но при этом сохраните определенный уровень взаимодействия.
barfoon

3
Что ж, я бы подумал, что это очевидно.
Люк Редпат

Я использую текстовые поля в качестве меток для изображений в игре, и я не хочу, чтобы увеличительное стекло появлялось, и нет причин копировать текст. Это отлично работает для меня. Я использую стилизованный текст в UIWebView, и там работает та же строка.
JScarry

15

Самый простой способ - создать подкласс UITextView, который переопределяет 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;
}

Это лучшее решение, позволяющее вводить текст, но ничего больше, и позволяет больше не «перехватывать» нажатие на текстовое поле. Это делает то, что хотел OP, и многое другое.
hlfcoding 05

13

Когда я возвращаю NO в canPerformAction на iOS 7, я получаю много таких ошибок:

<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.

Мое решение следующее:

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

Хитрость заключается в том, чтобы скрыть контроллер меню в следующем цикле в основной очереди (сразу после его отображения).


действительно мило. единственная проблема в том, что у меня есть 2 текстовых представления и одно текстовое поле, и я хочу избежать копирования и вставки только в полях textView. как определить, кто вызвал canPerformAction? переменная отправителя - UIMenuController
Gaucho

1
Он работает с подклассом UITextField (или UITextView). Если вы не используете созданный подкласс, это не даст никакого эффекта. Например, я создал TextFieldWithoutCopyPaste и использовал его там, где мне не хотелось использовать функцию копирования и вставки.
Адам Валлнер

хорошо ты прав. Но в моем случае для textView нужен подкласс для использования canPerformAction, а для textField нужен подкласс для использования textFieldDidBeginEditing для анимации окна при отображении клавиатуры. анимация перемещает окно с помощью клавиатуры. Я использую этот метод, чтобы клавиатура не закрывала текстовое поле.
Гаучо

10

Это самый простой способ отключить все меню выбора / копирования / вставки в UITextView.

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{    
    [UIMenuController sharedMenuController].menuVisible = NO;
    return NO;    
}

Также работает для UITextField. Спасибо, что поделился.
Gaurav Srivastava

1
Я использую вышеуказанный код, но у меня появляется меню, как его отключить, пожалуйста, помогите мне.
Bittoo,

4

Начиная с iOS 7 в UITextView есть свойство:

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

Это предотвращает возможность выделения текста в представлении. У меня отлично работает.


4

Если вы хотите заменить клавиатуру, скажем, UIPickerна inputView(с, конечно, с панелью инструментов inputAccesotyView), то этот обходной путь может помочь ...

  • Воплощать в жизнь textFieldShouldBeginEditing:
  • внутри положить textField.userInteractionEnabled = NO;
  • Затем, когда вы собираетесь закрыть UIPickerView, установите для него ДА.

Сделав это, вы сможете нажать UITextFieldи отобразить варианты выбора UIPickerView, в это время вы UITextFieldдействительно не будете реагировать на какое-либо событие касания (это включает касание и удерживание для вырезания, копирования и вставки). Однако вам нужно будет не забыть установить его обратно на ДА, когда вы закрываете свой, UIPickerViewоднако вы не сможете получить доступ к своемуUIPickerView снова.

Единственный момент, когда он терпит неудачу, - это когда пользователь начинает с нажатия и удерживания UITextView, тогда вы увидите вырезанную копию и снова вставку в первый раз. Вот почему вы всегда должны проверять свои вводные данные. Это самое простое, что я могу придумать. Другой вариант заключался в использовании UILabelдля текста, доступного только для чтения, но вы упускаете из виду большую функциональность UITextView.


4

Подкласс UITextView - swift 4.0

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

Как странно? он по-прежнему показывает опцию «Вставить», но не делайте этого при нажатии ...
iOS Flow

4

Если вы хотите отключить всплывающее окно, UITextFieldпопробуйте этот UITextFieldDelegateметод для переключения isUserInteractionEnabled.

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

3

Это легко сделать в раскадровке (Xcode 6). Просто снимите флажок «Редактируемый и выбираемый» в инспекторе атрибутов. Вы все еще можете прокручивать текстовое представление.введите описание изображения здесь


3

Это сработало для меня. Убедитесь, что вы вызываете resignFirstResponder в textView

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

2

Я предоставил рабочий ответ здесь , чтобы отключить выделение текста + лупу, хранение позволило clikable ссылки Надежды , что помогает:

После довольно долгих попыток мне удалось остановить выделение текста, увеличение и сохранение данных (ссылки доступны для кликов и т. Д.), Переопределив addGestureRecognizer в подклассе UITextView, позволяя только UILongPressGestureRecognizer задерживать завершение касания:

UIUnselectableTextView.m

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


2

Вы можете исправить это в раскадровке, сняв флажки со следующих полей:

введите описание изображения здесь

Или вы можете установить программно так:

textView.selectable = false
textView.editable = false

1

Я сделал это. На моем UITextViewя очень легко отключил возможность вырезать, копировать, выбирать и т. Д.

Я поместил a UIViewв то же место, где я разместил UITextView, но self.viewи добавил следующий touchDelegateметод:

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

    }
}

и это сработало для меня. Спасибо.


1

Swift

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

Связанный

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.

1

Если вы хотите добавить настраиваемую опцию в свой UITextView, но отключить существующие функции, вот как вы это делаете в Swift 3 :

Чтобы отключить функцию копирования, вставки, вырезания, создайте подкласс и переопределите следующее:

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

В ViewController у вас есть CustomTextView, чтобы добавить следующие параметры:

  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)")

    }

1

Этот метод полностью отключит меню Select, Select All, Paste. Если вы по-прежнему получаете какое-то другое действие, просто добавьте его в условие if, как показано ниже.

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

0

Вы можете просто создать такую ​​категорию:

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

1
Это не лучший способ решить эту проблему. Во-первых, это влияет на все, так как это в категории. Во-вторых, использование связанных объектов для довольно простой задачи может впоследствии вызвать больше проблем с отладкой (почему это так работает, когда вы забыли, что вы сделали), чем принести прибыль. На мой взгляд, лучше избегать этого, когда это возможно. Делает код менее простым для поиска, отладки и понимания новым программистом в проекте.
Vive

0

Создание подклассов UITextViewи переопределение - (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer- еще одна возможность отключить нежелательные действия.

Используйте класс gestureRecognizer-object, чтобы решить, нужно ли добавлять действие или нет.


0

(SWIFT) Если вам нужно только базовое текстовое поле без параметров меню или увеличительного стекла, создайте подкласс UITextField, возвращающий false для gestureRecognizerShouldBegin:

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

        return false
    }
}

Это позволит обойти все функции касания в текстовом поле, но по-прежнему позволит вам использовать всплывающую клавиатуру для добавления / удаления символов.

Если вы используете раскадровку, просто назначьте вновь созданный класс текстовому полю или если вы создаете текстовое поле программно:

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

basic.becomeFirstResponder()

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)}

0

Swift 3

Для этого вам нужно создать подкласс своего UITextView и поместить этот метод.

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)
    }

0

UITextView имеет два свойства, которые будут делать то, что вам нужно: isSelectable и isEditable. .

Если для isEditable установлено значение false, пользователь не сможет редактировать текст, а для isSelectable установлено значение false, чтобы пользователь не выделял текст внутри вашего textView, поэтому это предотвратит отображение меню действий.


0

Пожалуйста, найдите образец кода для справки:

 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
    }

-1

Используйте func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { retrun bool }вместо 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
    }
}

Создайте новый класс с именем StopPasteAction.swift

import UIKit

class StopPasteAction: UITextField {

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

Добавьте новый класс класса с текущим TextField

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.