該博客同步于 <swift 帶復(fù)制功能的 Label | AndyCuiの博客>
產(chǎn)品需求需要對(duì) UILabel 展示的文本進(jìn)行復(fù)制操作,針對(duì)這一需求想出了兩種實(shí)現(xiàn)方式: 1.自定義控件,添加復(fù)制功能. 2.使用 UItextView 實(shí)現(xiàn).
通過(guò)自定義 UILabel 的子類(lèi)實(shí)現(xiàn)
- 創(chuàng)建 YTTCopyLabel, 使其繼承于 UILabel.
class YTTCopyLabel: UILabel {
}
- 使 YTTCopyLabel 可以進(jìn)行交互, 添加長(zhǎng)按手勢(shì)使其觸發(fā)復(fù)制事件
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)
}
- 在長(zhǎng)按事件中實(shí)現(xiàn)復(fù)制功能
使用 UIMenuController 彈出復(fù)制菜單.使用簡(jiǎn)介
@objc private func longPressAction(_ sender: UIGestureRecognizer) {
guard sender.state == .began else {
return
}
// 變?yōu)榈谝豁憫?yīng)者
self.becomeFirstResponder()
// 菜單控制器
let menuController = UIMenuController.shared
// 復(fù)制 item
let copyItem = UIMenuItem(title: "復(fù)制", action: #selector(copyText))
// 添加 item 到 menu 控制器
menuController.menuItems = [copyItem]
// 設(shè)置菜單控制器點(diǎn)擊區(qū)域?yàn)楫?dāng)前控件 bounds
menuController.setTargetRect(self.bounds, in: self)
// 菜單顯示器可見(jiàn)
menuController.setMenuVisible(true, animated: true)
}
- 確認(rèn) label 具有的操作能力
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copyText) {
return true
}
return false
}
- 實(shí)現(xiàn)將 label 內(nèi)容放到剪切板
@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))
運(yùn)行效果如下:
YTTCopyLabel.swift
通過(guò)設(shè)置 UITextView 的屬性實(shí)現(xiàn)
UITextView 本身就有復(fù)制的功能,他有兩個(gè)屬性车海,一個(gè)可控制其是否編輯,一個(gè)是可控制其是否可選,只需將其可編輯設(shè)為 false
isEditable = false
- 創(chuàng)建 YTTCustomTextView,使其繼承與 UITextView
class YTTCustomTextView: UITextView {
}
- 重寫(xiě)父類(lèi)方法,使其使其編輯功能
@IBInspectable
var isAutoLayout: Bool = true // 是否是自適應(yīng)布局
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 // 設(shè)為 false 可自適應(yīng)
}
- 重寫(xiě)屬性 text 與 attributedText, 進(jìn)行自適應(yīng)布局
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
}
}
}
- 判斷屏幕點(diǎn)擊事件是否在本視圖,不是取消選中狀態(tài)
// 設(shè)置點(diǎn)擊空白取消選中效果
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 判斷點(diǎn)擊點(diǎn)是否在本視圖
if !self.point(inside: point, with: event) {
self.selectedRange = NSMakeRange(0, 0) // 設(shè)置為 NSMakeRange(0, 0) 取消選中效果
}
return super.hitTest(point, with: event)
}