给UITextField/UITextView增加limitLength属性

原创文章转载请注明出处


给UITextField/UITextView增加limitLength属性_第1张图片
Gyanam by Dharanish Kedarisetti on 500px.com

默认的UITextField并没有限制字符输入个数的属性,默认的做法是通过UITextFieldDelegate的接口去判断,每个使用UITextField的文件都要添加类似的代码,完全不符合程序员偷懒的性格。

Swift的Extensions比起Objective-C强大了许多,结合运行时的关联对象(associated objects),我们就来打造一个趁手的UITextField吧。

以下部分代码参考了Github上的开源代码,感谢开源的码农。

UITextField

import Foundation

//MARK: - UITextField

extension UITextField {
    
    public override class func initialize() {
        
        struct Static {
            static var token: dispatch_once_t = 0
        }
        
        // make sure this isn't a subclass
        if self !== UITextField.self {
            return
        }
        
        dispatch_once(&Static.token) {
            UITextFieldHelper.sharedInstance
        }
    }
    
    private struct AssociatedKeys {
        static var LimitLength = "LimitLength"
    }
    
    var limitLength: Int? {
        
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.LimitLength) as? Int
        }
        
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.LimitLength,
                    newValue as Int?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }
    
    func changeCharactersInRange(range: NSRange, replacementString string: String) -> String {
        assert((self.text! as NSString).length >= (range.location + range.length))
        let headPart = (self.text! as NSString).substringToIndex(range.location)
        let tailPart = (self.text! as NSString).substringFromIndex(range.location + range.length)
        let newString = "\(headPart)\(string)\(tailPart)"
        return newString
    }
}

//MARK: - UITextFieldHelper

private let singleTone = UITextFieldHelper()

class UITextFieldHelper: NSObject {
    
    /// 单例
    class var sharedInstance : UITextFieldHelper {
        return singleTone
    }
    
    override init() {
        super.init()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UITextFieldHelper.textFieldViewDidChange(_:)), name: UITextFieldTextDidChangeNotification, object: nil)
    }
    
    func textFieldViewDidChange(notification: NSNotification) {
        if let textField = notification.object as? UITextField {
            if let limit = textField.limitLength {
                if let _ = textField.markedTextRange {
                    //do nothing
                    return
                }
                
                if let text = textField.text {
                    if (text as NSString).length >= limit {
                        textField.text = (text as NSString).substringWithRange(NSMakeRange(0, limit))
                        textField.sendActionsForControlEvents(.EditingChanged)
                    }
                }
            }
        }
    }
}

如此一来,使用就非常方便了。直接设置UITextField的limitLength属性,超过设定的字符数时,会自动截取。UITextView的设置稍微有点不同,因为UITextView不支持ControlEvent。

UITextView

import Foundation

//MARK: - UITextView

extension UITextView {
    
    public override class func initialize() {
        
        struct Static {
            static var token: dispatch_once_t = 0
        }
     
        // make sure this isn't a subclass
        if self !== UITextView.self {
            return
        }
        
        dispatch_once(&Static.token) {
            UITextViewHelper.sharedInstance
        }
    }
    
    private struct AssociatedKeys {
        static var LimitLength = "LimitLength"
    }
    
    var limitLength: Int? {
        
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.LimitLength) as? Int
        }
        
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(
                    self,
                    &AssociatedKeys.LimitLength,
                    newValue as Int?,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }
    
    // Placeholder text
    var placeholder: String? {
        
        get {
            // Get the placeholder text from the label
            var placeholderText: String?
            
            if let placeholderLabel = self.viewWithTag(100) as? UILabel {
                placeholderText = placeholderLabel.text
            }
            return placeholderText
        }
        
        set {
            // Store the placeholder text in the label
            if let placeholderLabel = self.viewWithTag(100) as? UILabel {
                placeholderLabel.text = newValue
                placeholderLabel.sizeToFit()
                placeholderLabel.hidden = (self.text.length > 0)
            } else {
                self.addPlaceholderLabel(newValue!)
            }
        }
    }
    
    // Add a placeholder label to the text view
    func addPlaceholderLabel(placeholderText: String) {
        
        // Create the label and set its properties
        let placeholderLabel = UILabel()
        placeholderLabel.text = placeholderText
        placeholderLabel.sizeToFit()
        placeholderLabel.frame.origin.x = 5.0
        placeholderLabel.frame.origin.y = 5.0
        placeholderLabel.font = self.font
        placeholderLabel.textColor = UIColor.lightGrayColor()
        placeholderLabel.tag = 100
        
        // Hide the label if there is text in the text view
        placeholderLabel.hidden = (self.text.length > 0)
        
        self.addSubview(placeholderLabel)
    }
}

//MARK: - UITextViewHelper

private let singleTone = UITextViewHelper()

class UITextViewHelper: NSObject {
    
    /// 单例
    class var sharedInstance : UITextViewHelper {
        return singleTone
    }
    
    override init() {
        super.init()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UITextViewDelegate.textViewDidChange(_:)), name: UITextViewTextDidChangeNotification, object: nil)
    }
    
    func textViewDidChange(notification: NSNotification) {
        if let textView = notification.object as? UITextView {
            if let limit = textView.limitLength {
                if let _ = textView.markedTextRange {
                    //do nothing
                    return
                }
                
                if let text = textView.text {
                    if (text as NSString).length >= limit {
                        textView.text = (text as NSString).substringWithRange(NSMakeRange(0, limit))
                    }
                }
            }
            
            if let placeholderLabel = textView.viewWithTag(100) {
                if !textView.hasText() {
                    // Get the placeholder label
                    placeholderLabel.hidden = false
                }
                else {
                    placeholderLabel.hidden = true
                }
            }
        }
    }
}

上面的代码除了给UITextView添加了limitLength属性,还顺带添加了一个placeholder属性用于设置placeholder字符串。

我是咕咕鸡,一个还在不停学习的全栈工程师。
热爱生活,喜欢跑步,家庭是我不断向前进步的动力。

你可能感兴趣的:(给UITextField/UITextView增加limitLength属性)