Enable copy and paste on UITextField without making it editable
My final solution was the following:
I created a subclass of UILabel (UITextField should work the same) that displays a UIMenuController after being tapped. CopyableLabel.m looks like this:
@implementation CopyableLabel- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {if(action == @selector(copy:)) { return YES;}else { return [super canPerformAction:action withSender:sender];}}- (BOOL)canBecomeFirstResponder {return YES;}- (BOOL)becomeFirstResponder {if([super becomeFirstResponder]) { self.highlighted = YES; return YES;}return NO;}- (void)copy:(id)sender {UIPasteboard *board = [UIPasteboard generalPasteboard];[board setString:self.text];self.highlighted = NO;[self resignFirstResponder];}- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {if([self isFirstResponder]) { self.highlighted = NO; UIMenuController *menu = [UIMenuController sharedMenuController]; [menu setMenuVisible:NO animated:YES]; [menu update]; [self resignFirstResponder];}else if([self becomeFirstResponder]) { UIMenuController *menu = [UIMenuController sharedMenuController]; [menu setTargetRect:self.bounds inView:self]; [menu setMenuVisible:YES animated:YES];}}@end
This question is pretty old and I'm surprised nobody has posted a solution without subclassing. The idea presented in @mrueg's answer is correct, but you shouldn't need to subclass anything. I just came across this problem and solved it like this:
In my view controller:
- (void)viewDidLoad { self.textField.delegate = self; self.textField.text = @"Copyable, non-editable string.";}- (BOOL)canBecomeFirstResponder { return YES;}- (void)copyTextFieldContent:(id)sender { UIPasteboard* pb = [UIPasteboard generalPasteboard]; pb.string = self.textField.text;}- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { // UIKit changes the first responder after this method, so we need to show the copy menu after this method returns. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self becomeFirstResponder]; UIMenuController* menuController = [UIMenuController sharedMenuController]; UIMenuItem* copyItem = [[UIMenuItem alloc] initWithTitle:@"Copy" action:@selector(copyTextFieldContent:)]; menuController.menuItems = @[copyItem]; CGRect selectionRect = textField.frame; [menuController setTargetRect:selectionRect inView:self.view]; [menuController setMenuVisible:YES animated:YES]; }); return NO;}
If you want to make this work for a UILabel
, it should work the same way with just adding a tap gesture recognizer instead of using the delegate method.
This will do everything you need. Will be copyable. But not editable, and won't show a keyboard or a cursor.
class ViewController: UIViewController { @IBOutlet weak var copyableUneditableTextfield: UITextField! override func viewDidLoad() { super.viewDidLoad() copyableUneditableTextfield.delegate = self copyableUneditableTextfield.inputView = UIView() //prevents keyboard copyableUneditableTextfield.tintColor = .clear //prevents cursor copyableUneditableTextfield.text = "Some Text You Want User To Copy But Not Edit" }}extension ViewController: UITextFieldDelegate { func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { return false //prevents editing }}