Як відключити Copy, Cut, Select, Select All in UITextView


110

Функція UITextView"Копіювати", "Вирізати", "Вибрати", "Вибрати всі" відображається за замовчуванням, коли я натискаю на екрані. Але в моєму проекті UITextFieldце лише читання. Я не вимагаю цієї функціональності. Скажіть, будь ласка, як відключити цю функцію.


3
місце [UIMenuController sharedMenuController] .menuVisible = НІ; в - (BOOL) canPerformAction: (SEL) дія withSender: (id) метод відправника.
ravoorinandan

Відповіді:


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 | Визначити параметри. Я хочу відключити це повне меню.
Ameet Dhas

3
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; }- щоб заблокувати всі варіанти
Іслам Q.

Це чудово, однак я не знаю, як це зробити для UISearchBar - так як всередині є текстове поле, я думав, що можу перезаписати метод підкласу UISearchBar, але це, на жаль, не працює. Якісь ідеї для цього?
borchero

у мене є багато елементів управління. як дозволити копіювати пасту лише на одному елементі управління?
Гаучо

Це не працює для UITextView, хоча я бачив таке рішення, яке пропонується в ряді місць на SO. Хтось придумав, як це зробити?
Кормочка

67

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

- (BOOL)canBecomeFirstResponder {
    return NO;
}

Зауважте, що це стосується лише редакторів UITextView, які не можна редагувати! Не перевірив його на редагованих ...


Я думаю , що return NO;по - (BOOL)canPerformAction:(SEL)action withSender:(id)senderметоду є найкращим варіантом.
Іслам Q.

29

Якщо ви хочете відключити вирізати / скопіювати / вставити у всі UITextView свої програми, ви можете використовувати категорію з:

@implementation UITextView (DisableCopyPaste)

- (BOOL)canBecomeFirstResponder
{
    return NO;
}

@end

Це економить підкласику ... :-)


3
Ви також можете помістити це лише у свій /, файл, де вам потрібна така поведінка.
markus_p

4
Це працює лише як побічний ефект і заважає UITextViewповодитись так, як очікувалося, коли, наприклад, його можна редагувати і отримувати дотик. Це набагато перевизначити canPerformAction:withSender:; ось для чого складається протокол.
jdc

16
Ви не можете безпечно замінити метод, використовуючи категорію. Це невизначена поведінка. Ви повинні підклас, щоб безпечно перекрити метод.
Роб Нап’є

2
Примітка. Це стосується всіх UITextView у вашій програмі. Не ідеально більшу частину часу.
bbrame

2
Також зауважте, що Apple радить проти цього : "Якщо назва методу, оголошеного в категорії, збігається з методом у початковому класі, або методом в іншій категорії того ж класу (або навіть суперкласу), поведінка є не визначено, який спосіб використання використовується під час виконання ... [і] може спричинити проблеми при використанні категорій для додавання методів до стандартних класів 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
Мені подобається підхід до стрічкових стрічок і тюків. Я хотів би, щоб я міг вас ще +1 :) Натомість, ось золота зірка: i.imgur.com/EXLFt1Z.jpg
jdc

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

Швидкий 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

20

Якщо для прокрутки вам не потрібен UITextView, найпростішим рішенням, яке не передбачає підкласифікації, є просто відключити взаємодію користувачів для перегляду тексту:

textField.userInteractionEnabled = NO;

2
Це забирає натискання на посилання тощо, якщо це те, що є у перегляді тексту. Слід зазначити, що це не дуже вдале рішення для того, щоб ховати приховати / скопіювати / вставити, а також увімкнути деякий рівень взаємодії.
барфун

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

Це найкраще рішення для можливості введення тексту, але нічого іншого, і дозволяє натисканням на текстовому полі більше не "перехоплюватися". Це робить те, чого хотіла ОП, та інше.
hlfcoding

13

Коли я поверну НІ в 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 перегляду тексту та одне textField, і я хочу уникати копіювання-вставлення лише у поля textView. як визначити особу, хто викликав canPerformAction? змінною відправника є UIMenuController
Гаучо,

1
Він працює над підкласом UITextField (або UITextView). Якщо ви не використовуєте створений підклас, це не матиме жодного ефекту. Наприклад, я створив TextFieldWithoutCopyPaste і використовував його там, де не хотів функціонувати вставку для копіювання.
Адам Уолнер

добре, ти маєш рацію. Але в моєму випадку textView потрібен підклас для використання canPerformAction, а textField потрібен підклас для використання textFieldDidBeginEditing, щоб анімувати вікно, коли клавіатура відображається. анімація переміщує вікно за допомогою клавіатури. Я використовую цей метод, щоб уникнути того, щоб клавіатура покривала textField.
Гаучо

10

Це найпростіший спосіб відключити все меню Select / Copy / Paste в 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 - швидкий 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 варіанті я дуже легко відключив опцію вирізати, скопіювати, вибрати тощо.

Я розмістив місце 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

Швидкий

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 + Вибір.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-об'єкта, щоб визначити, чи слід додавати дію чи ні.


0

(SWIFT) Якщо потрібно просто основне текстове поле з жодним із параметрів меню або лупою, тоді створіть підклас UITextField, повертаючи помилку в 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

Швидкий 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 .

Якщо встановити параметр "Єздатне на помилкове", ви не зможете користувачеві редагувати текст та встановити значення " Вибір неправдивого", ви не зможете користувачеві вибирати текст у вашому текстовому перегляді, тому це не дозволить відображати меню дій.


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.