ios 在 UILabel 上显示 iPhone 剪切复制粘贴菜单

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1246198/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 16:15:37  来源:igfitidea点击:

Show iPhone cut copy paste menu on UILabel

iosswiftiphonecocoa-touch

提问by

  1. Can we enable the cut copy paste menu for a UILabelas it is for a UITextField?

  2. If not, and I need to convert my UILabelto UITextField, how can I enable the cut copy paste menu and not allow the content to be modified?

  1. 我们可以启用剪切复制粘贴菜单的UILabel,因为它是一个UITextField

  2. 如果没有,我需要将我的转换UILabelUITextField,如何启用剪切复制粘贴菜单并且不允许修改内容?

回答by zoul

I got the copy?&?paste menu working on a UILabel, I just had to return YESfor canBecomeFirstResponderand later call [label becomeFirstResponder]when the said label was to come on screen. As for returning YESfrom canBecomeFirstResponder, you can create a custom subclass or patch UILabelusing a category:

我得到了拷贝?&?粘贴菜单上的工作UILabel,我不得不回YEScanBecomeFirstResponder,后来打电话[label becomeFirstResponder]时说,标签是来在屏幕上。至于YES从返回canBecomeFirstResponder,您可以UILabel使用类别创建自定义子类或补丁:

@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 GitHubthat shows how to display a simple pasteboard menu on an UILabel.

类别解决方案感觉有点hackish,但如果您知道自己在做什么,它可能比子类化更容易。我还在GitHub 上放置了一个示例项目,展示了如何在UILabel.

回答by pableiros

For Swift you have to implement this class:

对于 Swift,你必须实现这个类:

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
        self.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(self.showMenu)))
    }

    @objc func showMenu(sender: AnyObject?) {
        self.becomeFirstResponder()

        let menu = UIMenuController.shared

        if !menu.isMenuVisible {
            menu.setTargetRect(bounds, in: self)
            menu.setMenuVisible(true, animated: true)
        }
    }

    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 UILabelwith CopyableLabelclass

在您的故事板中,只需将UILabelwithCopyableLabel类子类化

回答by benvolioT

The sample project on githubdue 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:

由于@zoul 的回答,github 上示例项目是要走的路。在撰写本文时,该项目实际上并未在剪贴板(粘贴板)上放置任何内容。方法如下:

Change @zoul's implementation of this method to:

将此方法的@zoul 实现更改为:

- (void) copy:(id)sender {
    UIPasteboard *pboard = [UIPasteboard generalPasteboard];
    pboard.string = self.text;  
}

回答by Hyman

Swift 4 ? Xcode 9.2. By using UIMenuControllerwe can do it.

斯威夫特 4 ? Xcode 9.2。通过使用UIMenuController我们可以做到。

I have created IBDesignableCustom UILabelclass which you can assign on storyboard directly

我创建了IBDesignable自定义UILabel类,您可以直接在故事板上分配

@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:

输出:

enter image description here

在此处输入图片说明

回答by codeperson

I've made an open source UILabel subclass that shows a UIMenuController with a "Copy" option upon long press:

我制作了一个开源 UILabel 子类,它在长按时显示一个带有“复制”选项的 UIMenuController:

HTCopyableLabelon GitHub

GitHub 上的HTCopyableLabel

回答by zhbrass

I've forked zoul's sample project and added support for ARC (and a couple of other features) if anyone's still interested:

如果有人仍然感兴趣,我已经分叉了 zoul 的示例项目并添加了对 ARC(以及其他一些功能)的支持:

https://github.com/zhbrass/UILabel-Clipboard

https://github.com/zhbrass/UILabel-Clipboard

CopyLabel.h/.m should be what you're looking for

CopyLabel.h/.m 应该是你要找的

回答by Alex Reynolds

Override the UITextFieldinstance's textFieldShouldBeginEditingmethod, and set it to return NOin order to disable editing.

覆盖UITextField实例的textFieldShouldBeginEditing方法,并将其设置为返回NO以禁用编辑。

Take a look at the UITextFieldDelegateprotocol for more details.

查看UITextFieldDelegate协议以获取更多详细信息。

回答by mukaissi

If you have multiline text, you should use UITextView

如果你有多行文本,你应该使用 UITextView

Set the delegate:

设置委托:

func textView(_ textView: UITextView,
              shouldChangeTextIn range: NSRange,
              replacementText text: String) -> Bool {
    return false
}

And it should work magically :)

它应该神奇地工作:)

回答by Fattie

2019 ...

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 notsomething like "return linkTurnedOnCurrently".)

(请注意,这是不是有点像“回归linkTurnedOnCurrently”。)

回答by iOS

In Swift 5.0and Xcode 10.2

Swift 5.0Xcode 10.2 中

Add copy option to your UILabel directly in your ViewController.

直接在 ViewController 中向 UILabel 添加复制选项。

//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(_:))
}