Can we enable the cut copy paste menu for a
UILabel
as it is for aUITextField
?If not, and I need to convert my
UILabel
toUITextField
, how can I enable the cut copy paste menu and not allow the content to be modified?

- 16,698
- 6
- 112
- 113
-
Any luck with option number 2? I'm currently trying to wire a UILabel subclass to support a very simple copy menu option, it's not a particularly straight-forward process. – Billy Gray Jan 21 '10 at 14:41
-
@BillyGray ten years later, this is now easy - scroll down to the recent answer I put in with the current method. – Fattie Aug 03 '19 at 13:53
12 Answers
For Swift you have to implement this class:
import UIKit
class CopyableLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
self.sharedInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.sharedInit()
}
func sharedInit() {
self.isUserInteractionEnabled = true
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu))
self.addGestureRecognizer(gesture)
}
@objc func showMenu(_ recognizer: UILongPressGestureRecognizer) {
self.becomeFirstResponder()
let menu = UIMenuController.shared
let locationOfTouchInLabel = recognizer.location(in: self)
if !menu.isMenuVisible {
var rect = bounds
rect.origin = locationOfTouchInLabel
rect.size = CGSize(width: 1, height: 1)
menu.showMenu(from: self, rect: rect)
}
}
override func copy(_ sender: Any?) {
let board = UIPasteboard.general
board.string = text
let menu = UIMenuController.shared
menu.setMenuVisible(false, animated: true)
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.copy)
}
}
In your storyboard just subclass the UILabel
with CopyableLabel
class

- 14,932
- 12
- 99
- 105
-
1Fixed targetRect, https://gist.github.com/baryon/bd2d3e7fe1fa57d19dae1d88f7662c32 – Baryon Lee Feb 28 '19 at 06:54
I got the copy & paste menu working on a UILabel
, I just had to return YES
for canBecomeFirstResponder
and later call [label becomeFirstResponder]
when the said label was to come on screen. As for returning YES
from canBecomeFirstResponder
, you can create a custom subclass or patch UILabel
using a category:
@implementation UILabel (Clipboard)
- (BOOL) canBecomeFirstResponder
{
return YES;
}
@end
The category solution feels a bit hackish, but if you know what you’re doing it might be easier than subclassing. I have also put up a sample project on GitHub that shows how to display a simple pasteboard menu on an UILabel
.

- 102,279
- 44
- 260
- 354
-
1I've added some additional functionality to zoul's repo. https://github.com/zhbrass/UILabel-Clipboard – zhbrass Feb 10 '14 at 17:26
The sample project on github due to @zoul's answer is the way to go. At the time of this writing, that project does not actually put anything on the clipboard (pasteboard). here is how:
Change @zoul's implementation of this method to:
- (void) copy:(id)sender {
UIPasteboard *pboard = [UIPasteboard generalPasteboard];
pboard.string = self.text;
}

- 4,507
- 2
- 36
- 30
Swift 4 ☻ Xcode 9.2.
By using UIMenuController
we can do it.
I have created IBDesignable
Custom UILabel
class which you can assign on storyboard directly
@IBDesignable
class TapAndCopyLabel: UILabel {
override func awakeFromNib() {
super.awakeFromNib()
//1.Here i am Adding UILongPressGestureRecognizer by which copy popup will Appears
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(_:)))
self.addGestureRecognizer(gestureRecognizer)
self.isUserInteractionEnabled = true
}
// MARK: - UIGestureRecognizer
@objc func handleLongPressGesture(_ recognizer: UIGestureRecognizer) {
guard recognizer.state == .recognized else { return }
if let recognizerView = recognizer.view,
let recognizerSuperView = recognizerView.superview, recognizerView.becomeFirstResponder()
{
let menuController = UIMenuController.shared
menuController.setTargetRect(recognizerView.frame, in: recognizerSuperView)
menuController.setMenuVisible(true, animated:true)
}
}
//2.Returns a Boolean value indicating whether this object can become the first responder
override var canBecomeFirstResponder: Bool {
return true
}
//3.Here we are enabling copy action
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return (action == #selector(UIResponderStandardEditActions.copy(_:)))
}
// MARK: - UIResponderStandardEditActions
override func copy(_ sender: Any?) {
//4.copy current Text to the paste board
UIPasteboard.general.string = text
}
}
Output:

- 13,571
- 6
- 76
- 98
-
2You should use `guard recognizer.state == .began else { return }` that way it'll show up the menu controller after the duration, rather than after you lift your finger. The user is expecting that feedback while their finger is pressed. – Paul Solt Nov 05 '18 at 14:57
-
Works great, but ditto what Paul said, change .recognized to .began in the guard statement and it will popup the copy while you're still pressing. Thanks! – KeithB Dec 21 '18 at 05:31
I've made an open source UILabel subclass that shows a UIMenuController with a "Copy" option upon long press:
HTCopyableLabel on GitHub

- 8,050
- 5
- 32
- 51
I've forked zoul's sample project and added support for ARC (and a couple of other features) if anyone's still interested:
https://github.com/zhbrass/UILabel-Clipboard
CopyLabel.h/.m should be what you're looking for

- 191
- 1
- 10
In Swift 5.0 and Xcode 10.2
Add copy option to your UILabel directly in your ViewController.
//This is your UILabel
@IBOutlet weak var lbl: UILabel!
//In your viewDidLoad()
self.lbl.isUserInteractionEnabled = true
let longPress = UILongPressGestureRecognizer.init(target: self, action: #selector((longPressFunctin(_:))))
self.lbl.addGestureRecognizer(longPress)
//Write these all functions outside the viewDidLoad()
@objc func longPressFunctin(_ gestureRecognizer: UILongPressGestureRecognizer) {
lbl.becomeFirstResponder()
let menu = UIMenuController.shared
if !menu.isMenuVisible {
menu.setTargetRect(CGRect(x: self.lbl.center.x, y: self.lbl.center.y, width: 0.0, height: 0.0), in: view)
menu.setMenuVisible(true, animated: true)
}
}
override func copy(_ sender: Any?) {
let board = UIPasteboard.general
board.string = lbl.text
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(copy(_:))
}
If you want to generalize this further to work for more than one label in your view controller, including labels that are buried deep in the view hierarchy, do this:
Add a variable to your view controller
private var currentLabelToCopy : UILabel? = nil
For each label that you want to add the copy function to, do basically what the above answer says to do in viewDidLoad. Here's an example for a label inside a tableView cell:
cell?.detailTextLabel?.isUserInteractionEnabled = true
let longPress = UILongPressGestureRecognizer.init(target: self, action: #selector((longPressFunctin(_:))))
cell?.detailTextLabel?.addGestureRecognizer(longPress)
Now modify the functions shown above as follows
@objc func longPressFunctin(_ gestureRecognizer: UILongPressGestureRecognizer) {
if let lbl = gestureRecognizer.view as? UILabel
{
lbl.becomeFirstResponder()
let menu = UIMenuController.shared
if !menu.isMenuVisible {
let frame = lbl.frame
if let superview = lbl.superview
{
menu.setTargetRect(CGRect(x: frame.maxX - (lbl.width / 2), y: frame.maxY - (lbl.height / 2) - 5, width: 0.0, height: 0.0), in: superview)
menu.setMenuVisible(true, animated: true)
self.currentLabelToCopy = lbl
}
}
}
}
override func copy(_ sender: Any?) {
let board = UIPasteboard.general
if let lbl = self.currentLabelToCopy
{
board.string = lbl.text
}
}
These stay the same:
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(copy(_:))
}
Also see this if you have any problems: https://stackoverflow.com/a/23839272/826946

- 2,639
- 3
- 21
- 32

- 16,698
- 6
- 112
- 113
Swift 5.3 and SwiftUI
To make this work in SwiftUI we can use the method that pableiros created an combine that with a UIViewRepresentable
.
There are two updates that we need to make to the CopyableLabel
class as the following methods were deprecated in iOS 13.
.setTargetRect(_,in:)
.setMenutVisible(_,animated)
We can easily fix this by using the .showMenu(from:rect:)
method instead.
Here is the updated CopyableLabel
class.
class CopyableLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
self.sharedInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.sharedInit()
}
func sharedInit() {
self.isUserInteractionEnabled = true
self.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu)))
}
@objc func showMenu(sender: AnyObject?) {
self.becomeFirstResponder()
let menu = UIMenuController.shared
if !menu.isMenuVisible {
menu.showMenu(from: self, rect: self.bounds) // <- we update the deprecated methods here
}
}
override func copy(_ sender: Any?) {
let board = UIPasteboard.general
board.string = text
let menu = UIMenuController.shared
menu.showMenu(from: self, rect: self.bounds) // <- we update the deprecated methods here
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.copy)
}
}
Then to get this class to work with SwiftUI all we have to do is create a simple UIViewRepresentable
.
struct CopyableLabelView: UIViewRepresentable {
let text: String
private let label = CopyableLabel(frame: .zero)
init(text: String) {
self.text = text
}
func makeUIView(context: Context) -> UILabel {
// Set the text for the label
label.text = text
// Set the content hugging priority so the UILabel's view is
// kept tight to the text.
label.setContentHuggingPriority(.required, for: .horizontal)
label.setContentHuggingPriority(.required, for: .vertical)
return label
}
func updateUIView(_ uiView: UILabel, context: Context) {
// Handle when the text that is passed changes
uiView.text = text
}
}

- 26,706
- 9
- 85
- 101
Override the UITextField
instance's textFieldShouldBeginEditing
method, and set it to return NO
in order to disable editing.
Take a look at the UITextFieldDelegate
protocol for more details.

- 95,983
- 54
- 240
- 345
If you have multiline text, you should use UITextView
Set the delegate:
func textView(_ textView: UITextView,
shouldChangeTextIn range: NSRange,
replacementText text: String) -> Bool {
return false
}
And it should work magically :)

- 2,441
- 1
- 21
- 12
-
This isn’t always true. I have an expanding cell with multiline text and textviews are horrible to use for expanding cells after the text reaches a certain point. In that situation uilabels are much better – Lance Samaria Mar 14 '19 at 08:27
2019 ...
Save anyone typing:
public class SomeComplexCustomView: UIView {
@IBOutlet var oneOfYourLabels: UILabel!
... your other labels, boxes, etc
public func makeThatLabelCopyable() {
oneOfYourLabels.isUserInteractionEnabled = true
addGestureRecognizer(UITapGestureRecognizer(
target: self, action: #selector(self.copyMenu(sender:))))
addGestureRecognizer(UILongPressGestureRecognizer(
target: self, action: #selector(self.copyMenu(sender:))))
// or use oneOfYourLabels.addGesture... to touch just on that item
}
public override var canBecomeFirstResponder: Bool { return true }
@objc func copyMenu(sender: Any?) {
becomeFirstResponder()
UIMenuController.shared.setTargetRect(bounds, in: self)
// or any exact point you want the pointy box pointing to
UIMenuController.shared.setMenuVisible(true, animated: true)
}
override public func copy(_ sender: Any?) {
UIPasteboard.general.string = oneOfYourLabels.text
// or any exact text you wish
UIMenuController.shared.setMenuVisible(false, animated: true)
}
override public func canPerformAction(
_ action: Selector, withSender sender: Any?) -> Bool {
return (action == #selector(copy(_:)))
}
}
It's that easy!
One subtlety:
One detail for better engineering:
Notice we turn on first responder:
public override var canBecomeFirstResponder: Bool { return true }
Often, on a given screen with such a label, you either will or won't have a copyable link like this.
So you'll very likely have something like:
var linkTurnedOnCurrently: Bool = false
func doShowThatLink( blah ) {
linkAvailableOnThisScreen = true
... the various code above ...
}
func doShowThatLink( blah ) {
linkAvailableOnThisScreen = false
... perhaps de-color the link, etc ...
}
Thus, in fact instead of this:
public override var canBecomeFirstResponder: Bool { return true }
be sure to do this:
public override var canBecomeFirstResponder: Bool {
if linkTurnedOnCurrently { return true }
return super.canBecomeFirstResponder
}
(Note that it is not something like "return linkTurnedOnCurrently".)
-
1Thanks @rob ! Yes, it's a great formula. It's a good example that, on SO answers can get REALLY out of date! This is the way to do it today, and it's easy, thank goodness. – Fattie Aug 03 '19 at 13:35
-
1Actually @Rob , please look at the **detail I added on the end of the answer**. – Fattie Aug 03 '19 at 13:46
@benvolioT's github project is very good example for copying. And for paste, customize canPerformAction:withSender:
.
For more see example CopyPasteTile.

- 1
- 1

- 176
- 2
- 6
- 8