swift 带有复制功能的 UILabel

该博客同步于

产品需求需要对 UILabel 展示的文本进行复制操作,针对这一需求想出了两种实现方式: 1.自定义控件,添加复制功能. 2.使用 UItextView 实现.

通过自定义 UILabel 的子类实现

  • 创建 YTTCopyLabel, 使其继承于 UILabel.
class YTTCopyLabel: UILabel {
}
  • 使 YTTCopyLabel 可以进行交互, 添加长按手势使其触发复制事件

override var canBecomeFirstResponder: Bool { return true }

override func awakeFromNib() {
    super.awakeFromNib()
    setup()
}

override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

private func setup() {
    self.isUserInteractionEnabled = true
    let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
    self.addGestureRecognizer(longPressGestureRecognizer)
}
  • 在长按事件中实现复制功能
    使用 UIMenuController 弹出复制菜单.使用简介
@objc private func longPressAction(_ sender: UIGestureRecognizer) {

    guard sender.state == .began else {
    return
    }

    // 变为第一响应者
    self.becomeFirstResponder()

    // 菜单控制器
    let menuController = UIMenuController.shared
    // 复制 item
    let copyItem = UIMenuItem(title: "复制", action: #selector(copyText))
    // 添加 item 到 menu 控制器
    menuController.menuItems = [copyItem]
    // 设置菜单控制器点击区域为当前控件 bounds
    menuController.setTargetRect(self.bounds, in: self)
    // 菜单显示器可见
    menuController.setMenuVisible(true, animated: true)

}
  • 确认 label 具有的操作能力
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    if action == #selector(copyText) {
        return true
    }
    return false
}
  • 实现将 label 内容放到剪切板
@objc private func copyText() {
    UIPasteboard.general.string = self.text
}
  • 在需要的地方使用
let label = YTTCopyLabel(frame: CGRect(x: 20, y: 100, width: self.view.frame.width - 40, height: 30))

运行效果如下:
YTTCopyLabel.swift

通过设置 UITextView 的属性实现

UITextView 本身就有复制的功能,他有两个属性,一个可控制其是否编辑,一个是可控制其是否可选,只需将其可编辑设为 false

isEditable = false
  • 创建 YTTCustomTextView,使其继承与 UITextView
class YTTCustomTextView: UITextView {
}
  • 重写父类方法,使其使其编辑功能
@IBInspectable
var isAutoLayout: Bool = true  // 是否是自适应布局

override func awakeFromNib() {
    super.awakeFromNib()
    setup()
}

override init(frame: CGRect, textContainer: NSTextContainer?) {
    super.init(frame: frame, textContainer: textContainer)
    setup()
}

private func setup() {
    self.isEditable = false
    self.isScrollEnabled = false // 设为 false 可自适应
}
  • 重写属性 text 与 attributedText, 进行自适应布局
override var text: String! {
    get {
        return super.text
    }
    set {
        super.text = newValue
        if !isAutoLayout {
            let width = self.bounds.width - (self.contentInset.left + self.contentInset.right + self.textContainerInset.left + self.textContainerInset.right + self.textContainer.lineFragmentPadding * 2)
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.lineBreakMode = self.textContainer.lineBreakMode
            let wordHeight = (self.text as NSString).boundingRect(with: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font : self.font!, .paragraphStyle: paragraphStyle ], context: nil).height + self.textContainerInset.top + self.textContainerInset.bottom + self.textContainer.lineFragmentPadding * 2 + self.contentInset.top + self.contentInset.bottom
            let rect = CGRect(x: self.frame.minX, y: self.frame.minY, width: width, height: wordHeight)
            self.frame = rect
        }
    }
}

override var attributedText: NSAttributedString! {
    get {
        return super.attributedText
    }
    set {
        super.attributedText = newValue
        if !isAutoLayout {
            let width = self.bounds.width - (self.contentInset.left + self.contentInset.right + self.textContainerInset.left + self.textContainerInset.right + self.textContainer.lineFragmentPadding * 2)
            let wordHeight = newValue.boundingRect(with: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).height + self.textContainerInset.top + self.textContainerInset.bottom + self.textContainer.lineFragmentPadding * 2 + self.contentInset.top + self.contentInset.bottom
            let rect = CGRect(x: self.frame.minX, y: self.frame.minY, width: width, height: wordHeight)
            self.frame = rect
        }
    }
}
  • 判断屏幕点击事件是否在本视图,不是取消选中状态
// 设置点击空白取消选中效果
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    // 判断点击点是否在本视图
    if !self.point(inside: point, with: event) {
        self.selectedRange = NSMakeRange(0, 0) // 设置为 NSMakeRange(0, 0) 取消选中效果
    }
    return super.hitTest(point, with: event)
}

YTTCustomTextView

你可能感兴趣的:(swift 带有复制功能的 UILabel)