56
  1. 我们可以为 a 启用剪切复制粘贴菜单UILabelUITextField

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

4

12 回答 12

49

对于 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
        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)
    }
}

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

于 2017-03-03T20:06:39.817 回答
40

我的复制和粘贴菜单在 a 上工作,当所述标签出现在屏幕上时UILabel,我只需要返回并稍后调用。至于从中返回,您可以使用类别创建自定义子类或补丁:YEScanBecomeFirstResponder[label becomeFirstResponder]YEScanBecomeFirstResponderUILabel

@implementation UILabel (Clipboard)

- (BOOL) canBecomeFirstResponder
{
    return YES;
}

@end

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

于 2010-04-16T13:55:28.257 回答
25

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

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

- (void) copy:(id)sender {
    UIPasteboard *pboard = [UIPasteboard generalPasteboard];
    pboard.string = self.text;  
}
于 2011-02-15T05:46:22.113 回答
12

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

我创建了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
    }
}

输出:

在此处输入图像描述

于 2018-03-05T14:02:21.703 回答
6

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

GitHub 上的HCopyableLabel

于 2013-07-30T15:33:25.180 回答
5

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

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

CopyLabel.h/.m 应该是您要查找的内容

于 2013-04-26T15:12:34.820 回答
2

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

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

于 2009-08-07T19:01:22.247 回答
2

Swift 5.3 和 SwiftUI

为了在 SwiftUI 中实现这一点,我们可以使用pableiros创建的方法 combine that with a UIViewRepresentable.

我们需要对CopyableLabel类进行两项更新,因为以下方法在 iOS 13 中已弃用。

.setTargetRect(_,in:)

.setMenutVisible(_,animated)

我们可以通过使用该.showMenu(from:rect:)方法轻松解决此问题。

这是更新的CopyableLabel课程。

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)
    }
}

然后为了让这个类与 SwiftUI 一起工作,我们所要做的就是创建一个简单的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
    }
}
  
于 2020-07-19T10:04:56.283 回答
2

Swift 5.0Xcode 10.2中

直接在您的 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(_:))
}
于 2019-04-23T06:11:48.653 回答
1

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

设置委托:

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

它应该神奇地工作:)

于 2018-04-30T23:44:58.340 回答
1

2019...

保存任何人输入:

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

就这么简单!


一个微妙之处:

更好的工程设计的一个细节:

请注意,我们打开了第一响应者:

 public override var canBecomeFirstResponder: Bool { return true }

通常,在具有此类标签的给定屏幕上,您将拥有或不会拥有这样的可复制链接。

所以你很可能会有类似的东西:

var linkTurnedOnCurrently: Bool = false

func doShowThatLink( blah ) {
    linkAvailableOnThisScreen = true
    ... the various code above ...
}

func doShowThatLink( blah ) {
    linkAvailableOnThisScreen = false
    ... perhaps de-color the link, etc ...
}

因此,实际上不是这样:

 public override var canBecomeFirstResponder: Bool { return true }

一定要这样做:

 public override var canBecomeFirstResponder: Bool {
    if linkTurnedOnCurrently { return true }
    return super.canBecomeFirstResponder
 }

(请注意,它不是“return linkTurnedOnCurrently”之类的东西。)

于 2019-07-11T19:20:41.223 回答
0

@benvolioT 的 github 项目是非常好的复制示例。对于粘贴,自定义canPerformAction:withSender:. 有关更多信息,请参见示例CopyPasteTile

于 2012-02-25T22:08:21.123 回答