Enable copy and paste on UITextField without making it editable

后端 未结 6 2000
孤街浪徒
孤街浪徒 2020-11-27 17:54

I want the text in a UITextField (or ideally, a UILabel) to be non-editable, but at the same time give the user the ability to cop

相关标签:
6条回答
  • 2020-11-27 18:04

    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
    
    0 讨论(0)
  • 2020-11-27 18:07

    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.

    0 讨论(0)
  • 2020-11-27 18:07

    Another solution is keeping the UITextField enabled but programmatically preventing it from being edited. This is done with the following delegate method:

    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    {
        return NO;
    }
    

    I'm not aware of possible limitations though, currently suits my needs.

    0 讨论(0)
  • 2020-11-27 18:08

    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
        }
    
    }
    
    0 讨论(0)
  • 2020-11-27 18:11

    The following code saved me.

    textField.addTarget(target, action: "textFieldEditingDidEndAction:", forControlEvents: [.EditingDidEnd])
    

    It seems Paste is a single and complete edit event.

    0 讨论(0)
  • 2020-11-27 18:24

    Try UITextView instead (I suspect it would work like a UILabel for you). I tested this with its editable property set to NO, and double-tapping-to-copy worked for me.

    0 讨论(0)
提交回复
热议问题