Swift:UITextView+Extension

作者: IIronMan | 来源:发表于2020-11-15 11:59 被阅读0次

    JKSwiftExtension,测试用例在 UITextViewExtensionViewController.swift 里面
    目录:
    1、基本的扩展 (使用runtime添加属性)

    一、基本的扩展 (使用runtime添加属性)
    // MARK: 提示:如果你想对textView.text直接赋值。请在设置属性之前进行,否则影响计算
    // MARK:- 一、基本的扩展 (使用runtime添加属性)
    public extension UITextView {
    
        // MARK: 1.1、设置占位符
        /// 设置占位符
        var placeholder: String? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.placeholder, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
            initPlaceholder(placeholder!)
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholder) as? String
            }
        }
    
        // MARK: 1.2、限制的字数
        /// 限制的字数
        var limitLength: NSNumber? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.limitLength, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
                initWordCountLabel(limitLength!)
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.limitLength) as? NSNumber
            }
        }
    
        // MARK: 1.3、限制行数
        /// 限制的行数
        var limitLines: NSNumber? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.limitLines, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
                NotificationCenter.default.addObserver(self, selector: #selector(limitLengthEvent), name: UITextView.textDidChangeNotification, object: self)
            }
           get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.limitLines) as? NSNumber
            }
        }
    
        // MARK: 1.4、默认文本字体的大小
        /// 默认文本字体的大小
        var placeholdFont: UIFont? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.placeholdFont, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                if self.placeholderLabel != nil {
                    self.placeholderLabel?.font = placeholdFont
                }
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholdFont) as? UIFont == nil ? UIFont.systemFont(ofSize: 13) : objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholdFont) as? UIFont
            }
        }
    
        // MARK: 1.5、默认文本的颜色
        /// 默认文本的颜色
        var placeholdColor: UIColor? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.placeholdColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                if self.placeholderLabel != nil {
                    self.placeholderLabel?.textColor = placeholdColor
                }
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholdColor) as? UIColor == nil ? UIColor.lightGray : objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholdColor) as? UIColor
            }
        }
    
        // MARK: 1.6、设置 默认文本的Origin
        /// 设置 默认文本的Origin
        var placeholderOrigin: CGPoint? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.placeholderOrigin, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                if self.placeholderLabel != nil && placeholderOrigin != nil {
                    self.placeholderLabel?.frame.origin = placeholderOrigin!
                }
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholderOrigin) as? CGPoint == nil ? CGPoint(x: 7, y: 7) : objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholderOrigin) as? CGPoint
            }
        }
    
        // MARK: 1.7、是否自动变化高度
        /// 是否自动变化高度
        var autoHeight: Bool? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.autoHeight, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.autoHeight) as? Bool == nil ? false : objc_getAssociatedObject(self, UITextView.RuntimeKey.autoHeight) as? Bool
            }
        }
    }
    
    // MARK:- fileprivate 私有的内容
    extension UITextView {
    
        fileprivate struct RuntimeKey {
            static let placeholder: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "PLACEHOLDEL".hashValue)
            static let limitLength: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "LIMITLENGTH".hashValue)
            static let limitLines: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "LIMITLINES".hashValue)
            static let placeholderLabel: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "PLACEHOLDELABEL".hashValue)
            static let wordCountLabel: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "WORDCOUNTLABEL".hashValue)
            static let placeholdFont: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "PLACEHOLDFONT".hashValue)
            static let placeholdColor: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "PLACEHOLDCOLOR".hashValue)
            static let limitLabelColor: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "LIMITLABLECOLOR".hashValue)
            static let autoHeight: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "AUTOHEIGHT".hashValue)
            static let oldFrame: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "LODFRAME".hashValue)
            static let placeholderOrigin: UnsafeRawPointer! = UnsafeRawPointer.init(bitPattern: "PLACEHOLDERORIGIN".hashValue)
            // ...其他Key声明
        }
    
        /// 最初的 frame
        fileprivate var oldFrame: CGRect? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.oldFrame, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.oldFrame) as? CGRect
            }
        }
    
        /// 文字数量
        fileprivate var wordCountLabel: UILabel? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.wordCountLabel, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.wordCountLabel) as? UILabel
            }
        }
    
        /// 默认文本
        fileprivate var placeholderLabel: UILabel? {
            set {
                objc_setAssociatedObject(self, UITextView.RuntimeKey.placeholderLabel, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
            get {
                return objc_getAssociatedObject(self, UITextView.RuntimeKey.placeholderLabel) as? UILabel
            }
        }
    
        /// 占位符
        /// - Parameter placeholder: 占位符
        fileprivate func initPlaceholder(_ placeholder: String) {
        
            NotificationCenter.default.addObserver(self, selector: #selector(textChange(_:)), name: UITextView.textDidChangeNotification, object: self)
            self.placeholderLabel = UILabel()
            placeholderLabel?.font = self.placeholdFont
            placeholderLabel?.text = placeholder
            placeholderLabel?.numberOfLines = 0
            placeholderLabel?.lineBreakMode = .byWordWrapping
            placeholderLabel?.textColor = self.placeholdColor
            let rect = placeholder.boundingRect(with: CGSize(width: self.jk.width - placeholderOrigin!.x * 2, height: CGFloat(MAXFLOAT)), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font : self.placeholdFont!], context: nil)
            placeholderLabel?.frame = CGRect(x: placeholderOrigin!.x, y: placeholderOrigin!.y, width: self.jk.width - placeholderOrigin!.x * 2, height: rect.size.height)
            addSubview(self.placeholderLabel!)
            oldFrame = self.frame
            placeholderLabel?.isHidden = self.text.count > 0 ? true : false
        }
    
        ///  字数限制
        /// - Parameter limitLength:  字数限制
        fileprivate func initWordCountLabel(_ limitLength : NSNumber) {
        
            NotificationCenter.default.addObserver(self, selector: #selector(limitLengthEvent), name: UITextView.textDidChangeNotification, object: self)
            if wordCountLabel != nil {
                wordCountLabel?.removeFromSuperview()
            }
            wordCountLabel = UILabel(frame: CGRect(x: 0, y: self.frame.size.height - 20, width: self.frame.size.width - 10, height: 20))
            wordCountLabel?.textAlignment = .right
            wordCountLabel?.textColor = self.textColor
            wordCountLabel?.font = self.font
            if self.text.count > limitLength.intValue {
                self.text = (self.text as NSString).substring(to: limitLength.intValue)
            }
            wordCountLabel?.text = "\(self.text.count)/\(limitLength)"
            addSubview(wordCountLabel!)
            self.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
        }
    
        /// 动态监听
        /// - Parameter notification: 动态监听
        @objc fileprivate func textChange(_ notification : Notification) {
            let textView = notification.object as! UITextView;
            self.text = textView.text;
            if placeholder != nil {
                placeholderLabel?.isHidden = true
                if self.text.count ==  0 {
                    self.placeholderLabel?.isHidden = false
                }
            }
            if limitLength != nil {
                var wordCount = self.text.count
                if wordCount > (limitLength?.intValue)! {
                    wordCount = (limitLength?.intValue)!
                }
                let limit = limitLength!.stringValue
                wordCountLabel?.text = "\(wordCount)/\(limit)"
            }
            if autoHeight == true {
                let size = getStringPlaceSize(self.text, textFont: self.font!)
                UIView.animate(withDuration: 0.15) { [weak self] in
                    guard let weakSelf = self else { return }
                    JKPrint("autoHeight的CGPoint=========\(weakSelf.placeholderOrigin!)")
                    weakSelf.frame = CGRect(x: weakSelf.jk.x, y: weakSelf.jk.y, width: weakSelf.jk.width, height: size.height + weakSelf.placeholderOrigin!.y * 2 < weakSelf.oldFrame!.height ? weakSelf.oldFrame!.height : size.height + weakSelf.placeholderOrigin!.y * 2)
                }
            }
        }
    
        @objc fileprivate func limitLengthEvent() {
            if limitLength != nil {
                if self.text.count > (limitLength?.intValue)! && limitLength != nil {
                    self.text = (self.text as NSString).substring(to: (limitLength?.intValue)!)
                    JKPrint("Maximum number of words");
                }
            } else {
                //行数限制
                if (limitLines != nil) {
                    var size = getStringPlaceSize(self.text, textFont: self.font!)
                    let height = self.font!.lineHeight * CGFloat(limitLines!.floatValue)
                    if (size.height > height) {
                        JKPrint("Maximum number of lines");
                        //循环计算当前高度可以存放的字符
                        while size.height > height {
                            self.text = (self.text as NSString).substring(to: self.text.count - 1)
                            size = getStringPlaceSize(self.text, textFont: self.font!)
                        }
                    }
                }
            }
        }
    
        @objc fileprivate func getStringPlaceSize(_ string : String, textFont : UIFont) -> CGSize {
            ///计算文本高度
            let font : UIFont = textFont
            let attribute = [NSAttributedString.Key.font : font];
            let options = NSStringDrawingOptions.usesLineFragmentOrigin
            let size = string.boundingRect(with: CGSize(width: self.contentSize.width-10, height: CGFloat.greatestFiniteMagnitude), options: options, attributes: attribute, context: nil).size
            return size
        }
    
        open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
            if action == #selector(UIResponderStandardEditActions.paste(_:))
                || action == #selector(UIResponderStandardEditActions.select(_:))
                || action == #selector(UIResponderStandardEditActions.selectAll(_:))
                || action == #selector(UIResponderStandardEditActions.copy(_:))
                || action == #selector(UIResponderStandardEditActions.cut(_:))
                || action == #selector(UIResponderStandardEditActions.delete(_:))
        {
                OperationQueue.main.addOperation {
                    UIMenuController.shared.setMenuVisible(false, animated: false)
                }
                self.selectedRange = NSRange.init(location: 0, length: 0)
                return false
            }
            return super.canPerformAction(action, withSender: sender)
        }
    }
    

    相关文章

      网友评论

        本文标题:Swift:UITextView+Extension

        本文链接:https://www.haomeiwen.com/subject/cmrnbktx.html