How to disable pasting in a TextField in Swift?
SwiftUitextfieldPasteSwift Problem Overview
I've got a TextField
with a numberPad
and the function runs only if it contains numbers.
The user will crash the app if they paste letters in the TextField
and click OK.
How can I disable pasting in the TextField
?
Swift Solutions
Solution 1 - Swift
I agree with Leonardo Savio Dabus, if I were you I'd use string checking and just give out a warning, it makes things easier. BUT, if disabling paste option is a fancy feature you really want to put into your app, then you need to do more work. I'll provide the steps below.
Step 1: You need to create another class which extends the UITextField
. In this example, I made my CustomUITextField
.
import Foundation
import UIKit //Don't forget this
class CustomUITextField: UITextField {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.paste(_:)) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
Step 2: Wire the storyboard with your ViewController. You need to declare an IBOutlet
as in normal case:
@IBOutlet var textFieldA: CustomUITextField?
Wire the circle next to the @IBOutlet
to the TextField
in the storyboard. THEN, this is important and easy to be ignored:
- Go to your storyboard
- Click the target
TextField
- Select Identity Inspector (the 3rd one)
- Change the class to
CustomUITextField
Quick snapshot is provided below.
That's it, hope this works.
Credit:
If you want to know more about the behavior of canPerformAction
method, though it's an Objective-C version, the concepts are shared here.
Solution 2 - Swift
For Swift 3 it's changed to:
override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)) || action == #selector(paste(_:)) {
return false
}
return true
}
Solution 3 - Swift
For Swift 5
UIResponderStandardEditActions
has been added recently (iOS 10.0+) through which we can safely check if action is "paste" or not.
import UIKit
class NMTextField: UITextField {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.paste(_:)) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
Solution 4 - Swift
Details
- Xcode 9.1, Swift 4
- Xcode 10.2 (10E125), 11.2 (11B52), Swift 5
Solution 1
// class TextField: UITextField
extension UITextField {
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.cut) || action == #selector(UIResponderStandardEditActions.copy)
}
}
Solution 1 usage
let textField = UITextField(frame: CGRect(x: 50, y: 120, width: 200, height: 50))
textField.borderStyle = .roundedRect
view.addSubview(textField)
Solution 2
import UIKit
// MARK: Enable/Disable textfield longpress actions
enum ResponderStandardEditActions {
case cut, copy, paste, select, selectAll, delete
case makeTextWritingDirectionLeftToRight, makeTextWritingDirectionRightToLeft
case toggleBoldface, toggleItalics, toggleUnderline
case increaseSize, decreaseSize
var selector: Selector {
switch self {
case .cut:
return #selector(UIResponderStandardEditActions.cut)
case .copy:
return #selector(UIResponderStandardEditActions.copy)
case .paste:
return #selector(UIResponderStandardEditActions.paste)
case .select:
return #selector(UIResponderStandardEditActions.select)
case .selectAll:
return #selector(UIResponderStandardEditActions.selectAll)
case .delete:
return #selector(UIResponderStandardEditActions.delete)
case .makeTextWritingDirectionLeftToRight:
return #selector(UIResponderStandardEditActions.makeTextWritingDirectionLeftToRight)
case .makeTextWritingDirectionRightToLeft:
return #selector(UIResponderStandardEditActions.makeTextWritingDirectionRightToLeft)
case .toggleBoldface:
return #selector(UIResponderStandardEditActions.toggleBoldface)
case .toggleItalics:
return #selector(UIResponderStandardEditActions.toggleItalics)
case .toggleUnderline:
return #selector(UIResponderStandardEditActions.toggleUnderline)
case .increaseSize:
return #selector(UIResponderStandardEditActions.increaseSize)
case .decreaseSize:
return #selector(UIResponderStandardEditActions.decreaseSize)
}
}
}
class TextField: UITextField {
private var editActions: [ResponderStandardEditActions: Bool]?
private var filterEditActions: [ResponderStandardEditActions: Bool]?
func setEditActions(only actions: [ResponderStandardEditActions]) {
if self.editActions == nil { self.editActions = [:] }
filterEditActions = nil
actions.forEach { self.editActions?[$0] = true }
}
func addToCurrentEditActions(actions: [ResponderStandardEditActions]) {
if self.filterEditActions == nil { self.filterEditActions = [:] }
editActions = nil
actions.forEach { self.filterEditActions?[$0] = true }
}
private func filterEditActions(actions: [ResponderStandardEditActions], allowed: Bool) {
if self.filterEditActions == nil { self.filterEditActions = [:] }
editActions = nil
actions.forEach { self.filterEditActions?[$0] = allowed }
}
func filterEditActions(notAllowed: [ResponderStandardEditActions]) {
filterEditActions(actions: notAllowed, allowed: false)
}
func resetEditActions() { editActions = nil }
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if let actions = editActions {
for _action in actions where _action.key.selector == action { return _action.value }
return false
}
if let actions = filterEditActions {
for _action in actions where _action.key.selector == action { return _action.value }
}
return super.canPerformAction(action, withSender: sender)
}
}
Solution 2 usage
let textField = TextField(frame: CGRect(x: 50, y: 50, width: 200, height: 50))
textField.borderStyle = .roundedRect
view.addSubview(textField)
textField.setEditActions(only: [.copy, .cut, .paste])
//textField.filterEditActions(notAllowed: [.copy, .cut, .paste])
//textField.addToCurrentEditActions(actions: [.paste])
Full sample of the solution 2
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
addTextField(y: 50)
addTextField(y: 100).setEditActions(only: [.copy, .cut, .paste])
addTextField(y: 150).filterEditActions(notAllowed: [.copy, .cut, .paste])
}
@discardableResult func addTextField(y: CGFloat) -> TextField {
let textField = TextField(frame: CGRect(x: 50, y: y, width: 200, height: 34))
textField.borderStyle = .roundedRect
textField.text = "Text"
view.addSubview(textField)
return textField
}
}
Solution 5 - Swift
Swift 4.1 this code is working fine with ViewController.
-
Disable all option (copy, paste, delete.....etc)
extension UITextField {
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { return false }
}
-
Enable particular option (select, selectAll... etc)
extension UITextField {
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { return action == #selector(UIResponderStandardEditActions.select(:)) || action == #selector(UIResponderStandardEditActions.selectAll(:)) }
Solution 6 - Swift
Swift 5
If you want to block paste action from every text field in your app
extension UITextField {
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.paste(_:)) ?
false : super.canPerformAction(action, withSender: sender)
}
}
EDIT
If you want to block the paste action for text fields that have pickers as input view, add a guard
like the following:
extension UITextField {
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
guard inputView != nil else { return super.canPerformAction(action, withSender: sender) }
return action == #selector(UIResponderStandardEditActions.paste(_:)) ?
false : super.canPerformAction(action, withSender: sender)
}
}
Warning: the second solution will allow the user to paste on numeric fields.
Solution 7 - Swift
You can just attach an IBAction to your Sent Events (editing changed) of your textfield to remove all non digits from your text as you type as follow:
@IBAction func editingChanged(_ textField: UITextField) {
textField.text?.removeAll { !("0"..."9" ~= $0) }
}
This will allow the user to paste into the field but it will filter all non digits from the string.
Solution 8 - Swift
In the actual swift version(2.2 going to 3.0) this function code must be refactored to:
override public func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == #selector(NSObject.copy(_:)) || action == #selector(NSObject.paste(_:)) {
return false
}
return true
}
Solution 9 - Swift
class CustomUITextField: UITextField {
override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(cut(_:)) ||
action == #selector(copy(_:)) ||
action == #selector(UIResponderStandardEditActions.paste(_:)) ||
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
}
}
Solution 10 - Swift
You can create an extension for UITextField
and override canPerformAction
:
override public func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
return (action != "paste:")
}
Solution 11 - Swift
Small edit with a code because when you try to use any function like cut or another one the app will crash . The following code tested on swift 3 and working very well
override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)) || action == #selector(paste(_:)) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
Solution 12 - Swift
I have created a custom class for textField. I have handled the case when you want to enable/disable actions on textfield. You can customize the code as per your requirement. Set isActionsEnabled true/false for enable/disable actions on textfield.
Prefer to use
> return super.canPerformAction(action, withSender: sender)
instead of
> return true
because returning true might cause a crash in some cases.
Here is my code,
open class MyTextFieldEffect : UITextField {
var isActionsEnabled = true {
didSet {
reloadInputViews()
}
}
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
/* disable particular actions
if (action == #selector(paste(_:)) || action == #selector(copy(_:)) || action == #selector(select(_:)) || action == #selector(cut(_:)) || action == #selector(delete(_:)) || action == #selector(replace(_:withText:)) || action == #selector(select(_:)) || action == #selector(selectAll(_:)) || action == #selector(insertText(_:)) || action == #selector(draw(_:))) && !isActionsEnabled {
return false
}
return super.canPerformAction(action, withSender: sender)
*/
//disable all actions
if !isActionsEnabled {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
Solution 13 - Swift
If you want to Open Date Picker or Picker view on TEXTFIELD click then below code work.
Add below two methods in your class.
//Hide Menu View
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if YOURTEXTFIELD.isFirstResponder {
DispatchQueue.main.async(execute: {
(sender as? UIMenuController)?.setMenuVisible(false, animated: false)
})
return false
}
return super.canPerformAction(action, withSender: sender)
}
//MUST Implement
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return false
}
Solution 14 - Swift
I did use this code. This is working.
override func canPerformAction(_ action: Selector, withSender sender: Any?) Bool {
if YOURTEXTFIELD.isFirstResponder {
DispatchQueue.main.async(execute: {
(sender as? UIMenuController)?.setMenuVisible(false, animated: false)
})
return false
}
return super.canPerformAction(action, withSender: sender)
}