UITextView的Placeholder

UITextView 没有placeholder 妹的办法,只好自己实现,Swift 版本主要通过继承UIView,在UIView上添加UITextView 和 一个UILabel实现placeholder,详情看代码,OC实现也差不多。

OC版本使用Runtime 运行时属性添加一个placeholder,和展示placeholder的UILabel 

工作上用到的主要Swift 版本,稍微完善一些,Xib中所见即所得已实现,关键字类@IBDesignable 属性 @IBInspectable ,OC高逼格版用可以,不够完善 

1. Swift 版本 

importFoundation

import UIKit

@IBDesignable class PSTextView: UIView,UITextViewDelegate {

    private var ps_placeholder =""

    private var ps_maxLength :Int=0

    private var ps_isShowNum :Bool=false

    private var ps_borderWidth :CGFloat=1

    private var ps_borderColor :UIColor=UIColor.groupTableViewBackground

    private var ps_cornerRadius :CGFloat=5

    private var ps_font :UIFont=UIFont.systemFont(ofSize:14.0)

    private var ps_textColor :UIColor=UIColor.black

    private var ps_placeholderColor :UIColor=UIColor.lightGray

    private var ps_text :String=""


    private let numFont :UIFont=UIFont.systemFont(ofSize:10)

    private let numHeight :CGFloat=12.0

    private let numColor :UIColor  =UIColor.gray

    private var backgroundView :UIView?// 背景图

    private var textView :UITextView?// textView

    private var placeholderLb :UILabel?// placeholder label

    private var numberLb :UILabel?// 计数器label

    override init(frame:CGRect) {//  类使用了@IBDesignable 初始化 --自定义属性的set方法-->draw(_ rect: CGRect),不再执行init(frame: CGRect)

        super.init(frame: frame)

    }

    required init?(coder aDecoder:NSCoder) {

        super.init(coder: aDecoder)

        //        draw(bounds)

    }

    override func draw(_rect:CGRect) {

        load() // 初始化

    }


    // 添加 UITextView

    private func load() {

        layer.cornerRadius = ps_cornerRadius

        layer.borderWidth = ps_borderWidth

        layer.borderColor = ps_borderColor.cgColor

        ps_initView()

        ps_textViewChange() // xib 中更改属性要走这里

        ps_setNumberHidden(hidden: ps_isShowNum) // 设置是否展示最大计数

    }

}

//MARK: ============= setter  and  getter

extension PSTextView {

    // 装 系列View 的容器

    private func ps_initView() {

        textView = UITextView.init(frame:CGRect.init(x:0, y:0, width:self.bounds.width, height:self.bounds.height))

        textView?.font = ps_font

        textView?.delegate=self

        textView?.textColor = ps_textColor

        textView?.returnKeyType= .done

        textView?.backgroundColor = .clear

        textView?.text=text

        placeholderLb=UILabel.init()

        placeholderLb?.textColor = ps_placeholderColor

        placeholderLb?.numberOfLines = 0

        placeholderLb?.font = ps_font

        placeholderLb?.text = ps_placeholder

        numberLb = UILabel.init(frame:CGRect.init(x:0, y: (textView?.bounds.height)! -numHeight, width: (textView?.bounds.width)!, height:numHeight))

        numberLb?.font = numFont

        numberLb?.textColor = numColor

        numberLb?.text = "0/\(ps_maxLength)"

        numberLb?.textAlignment= .right

        numberLb?.isHidden=true

        backgroundView=UIView.init(frame:self.bounds)

        backgroundView?.addSubview(placeholderLb!)

        backgroundView?.addSubview(textView!)

        backgroundView?.addSubview(numberLb!)

        addSubview(backgroundView!)

        // 关闭 autoresizing

        // 自动布局 本来想设置和textView,一样大的 但是placeholder 的text 显示就不正确了,so 使用自动布局 效果还是很不错滴😆

        placeholderLb?.translatesAutoresizingMaskIntoConstraints = false

        lettextInset =textView?.textContainerInset

        let linePadding = textView?.textContainer.lineFragmentPadding

        letx = linePadding! + (textInset?.left)!

        lety = textInset?.top

        let left :NSLayoutConstraint=NSLayoutConstraint.init(item:placeholderLb!, attribute:NSLayoutAttribute.left, relatedBy:NSLayoutRelation.equal, toItem:backgroundView!, attribute:NSLayoutAttribute.left, multiplier:1.0, constant: x)

        let top :NSLayoutConstraint=NSLayoutConstraint.init(item:placeholderLb!, attribute:NSLayoutAttribute.top, relatedBy:NSLayoutRelation.equal, toItem:backgroundView!, attribute:NSLayoutAttribute.top, multiplier:1.0, constant: y!)

        let right :NSLayoutConstraint=NSLayoutConstraint.init(item:placeholderLb!, attribute:NSLayoutAttribute.right, relatedBy:NSLayoutRelation.equal, toItem:backgroundView!, attribute:NSLayoutAttribute.right, multiplier:1.0, constant: -textInset!.right)

        placeholderLb?.superview!.addConstraint(left)

        placeholderLb?.superview!.addConstraint(right)

        placeholderLb?.superview!.addConstraint(top)

    }

    /**

     * 占位字符串

     */

    @IBInspectable var placeholder:String{

        get{

            return ps_placeholder

        }

        set{

            ps_placeholder= newValue

            placeholderLb?.text = ps_placeholder

        }

    }

    /**

     * 最多输入多少个字符串

     */

    @IBInspectable var maxLength:Int{

        get{

            return  ps_maxLength

        }

        set{

            ps_maxLength= newValue

        }

    }

    /**

     * 是否展示数字限制label

     */

    @IBInspectable var isShowNum:Bool{

        get{

            returnps_isShowNum

        }set{

            ps_isShowNum= newValue

            ps_setNumberHidden(hidden: newValue)

        }

    }

    /**

     * 边界宽度 默认是1

     */

    @IBInspectable  varborderWidth:CGFloat{

        get{

            return ps_borderWidth

        }

        set{

            ps_borderWidth= newValue

            layer.borderWidth= newValue

        }

    }

    /**

     * 边界线颜色 默认UIColor.groupTableViewBackground

     */

    @IBInspectablevarborderColor:UIColor{

        get{

            return ps_borderColor

        }

        set{

            ps_borderColor= newValue

            layer.borderColor= newValue.cgColor

        }

    }

    /**

     * 圆角 默认5

     */

    @IBInspectable var cornerRadius:CGFloat{

        get{

            return ps_cornerRadius

        }

        set{

            ps_cornerRadius= newValue

            layer.cornerRadius= newValue

        }

    }

    /**

     * 字体 默认  系统字体 14号

     */

    @IBInspectable var font: UIFont {

        get{

            returnps_font

        }

        set{

            ps_font= newValue

            placeholderLb?.font = ps_font

            textView?.font=ps_font

        }

    }

    /**

     * text View 颜色 默认 黑色

     */

    @IBInspectable var textColor:UIColor{

        get{

            returnps_textColor

        }

        set{

            ps_textColor= newValue

            textView?.textColor = ps_textColor

        }

    }

    /**

     *  placeholder 颜色

     */

    @IBInspectable var placeholderColor:UIColor{

        get{

            return ps_placeholderColor

        }

        set{

            ps_placeholderColor= newValue

            placeholderLb?.textColor= newValue

        }

    }

    /**

     * 设置 text

     */

    @IBInspectable  var text : String {

        get{

            returnps_text

        }set{

            textView?.text= newValue

            ps_text= newValue

            ps_textViewChange()

        }

    }

}

//MARK: ============= textView Delegate

extension PSTextView {

    private func ps_setNumberHidden(hidden :Bool) {

        if hidden {

            numberLb?.isHidden=false

            vartvframe =textView?.frame

            tvframe?.size.height-=numHeight

            textView?.frame= tvframe!

        }else{

            numberLb?.isHidden=true

            vartvframe =textView?.frame

            tvframe?.size.height-=self.frame.height

            textView?.frame= tvframe!

        }

    }

    // textView text 改变

    private func ps_textViewChange() {

        numberLb?.text = "\((textView?.text.count)!)/\(ps_maxLength)"

        placeholderLb?.isHidden= ((textView?.text.count)! >0)

    }

    func text ViewDidChange(_textView:UITextView) {

        ps_textViewChange()

        ps_text= textView.text

    }

    func textView(_textView:UITextView, shouldChangeTextIn range:NSRange, replacementText text:String) ->Bool{

        if text =="\n"{

            ps_resignFirstResponder()

            return false

        }else{

        }

        if ps_maxLength==0{

            return true

        }

        let length = textView.text.count- range.length+ text.count

        return length <=ps_maxLength

    }

    /**

     * 失去第一响应

     */

    func ps_resignFirstResponder()  {

        textView?.resignFirstResponder()

    }

    /**

     * 成为第一响应者

     */

    func ps_becomeFirstResponder()  {

        textView?.becomeFirstResponder()

    }

}


2. OC 高逼格的版本,属性暂时就想了这么多

 @interface UITextView (PSPlaceholder)

/**

 *  UITextView+placeholder

 */

@property(nonatomic,copy)NSString*ps_placeHolder;

/**

 *  placeHolder颜色

 */

@property(nonatomic,strong)UIColor*ps_placeHolderColor;

@end

.m 文件

//

//  UITextView+PSPlaceholder.m

//  hehe

//  Created by Peng on 2018/4/19.

//  Copyright © 2018年 PengShuai. All rights reserved.

//

#import "UITextView+PSPlaceholder.h"

#import

static char ps_placeholderKey;

static char ps_placeholderLbKey; // property keyword

@interface UITextView()

@property (nonatomic, readonly) UILabel *placeholderLabel;

@end

@implementation UITextView (PSPlaceholder)

+ (void)load {

    [superload];

    // exchange layout Subviews 设置placeholderLb 的frame

    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"layoutSubviews")), class_getInstanceMethod(self.class, @selector(ps_layoutSubviews)));

    // exchange dealloc 方法 移除通知

    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")),class_getInstanceMethod(self.class,@selector(ps_dealloc)));

    // exchange setText 方法

    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"setText:")),class_getInstanceMethod(self.class,@selector(ps_setText:)));

}

// 设置placeholderLabel frame

- (void) ps_layoutSubviews {

    if (self.ps_placeHolder) {

        UIEdgeInsets textContainerInset =self.textContainerInset;

        CGFloat lineFragmentPadding = self.textContainer.lineFragmentPadding;

        CGFloatx = lineFragmentPadding +                    textContainerInset.left+self.layer.borderWidth;

        CGFloaty = textContainerInset.top+self.layer.borderWidth;

        CGFloatwidth =CGRectGetWidth(self.bounds) - x - textContainerInset.right-2*self.layer.borderWidth;

        CGFloatheight = [self.placeholderLabel sizeThatFits:CGSizeMake(width,0)].height;

        self.placeholderLabel.frame=CGRectMake(x, y, width, height);

    }

    [self ps_layoutSubviews];

}

// 移除通知

- (void) ps_dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    [self ps_dealloc];

}

// 设置 text

- (void) ps_setText:(NSString*) text {

    [self ps_setText:text];

    if (self.ps_placeHolder) {

        [self ps_updatePlaceHolder];

    }

}

#pragma mark ===========  get and  setter

- (UILabel*)placeholderLabel {

    UILabel*placeholderLb =objc_getAssociatedObject(self, &ps_placeholderLbKey);

    if(!placeholderLb) {

        placeholderLb = [[UILabelalloc]init];

        placeholderLb.numberOfLines=0;

        placeholderLb.textColor= [UIColor lightGrayColor];

        objc_setAssociatedObject(self, &ps_placeholderLbKey, placeholderLb, OBJC_ASSOCIATION_RETAIN);

   [[NSNotificationCenterdefaultCenter] addObserver:selfselector:@selector(ps_updatePlaceHolder) name:UITextViewTextDidChangeNotification object:self];

    }

    returnplaceholderLb;

}

- (NSString*)ps_placeHolder {

   return objc_getAssociatedObject(self, &ps_placeholderKey);

}

- (void)setPs_placeHolder:(NSString*)ps_placeHolder {

    objc_setAssociatedObject(self, &ps_placeholderKey, ps_placeHolder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    [self ps_updatePlaceHolder];

}

- (UIColor*)ps_placeHolderColor {

    return self.placeholderLabel.textColor;

}

- (void)setPs_placeHolderColor:(UIColor*)ps_placeHolderColor {

    self.placeholderLabel.textColor= ps_placeHolderColor;

}

#pragma mark - update

- (void)ps_updatePlaceHolder{

    if(self.text.length) {

        [self.placeholderLabel removeFromSuperview];

        return;

    }

    self.placeholderLabel.font = self.font?self.font:self.ps_getDefaultFont;

    self.placeholderLabel.textAlignment = self.textAlignment;

    self.placeholderLabel.text = self.ps_placeHolder;

    [self insertSubview:self.placeholderLabel atIndex:0];

}

// 获取默认字体

- (UIFont*) ps_getDefaultFont{

    static UIFont*font =nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        UITextView*textview = [[UITextViewalloc]init];

        textview.text=@" ";

        font = textview.font;

    });

    returnfont;

}

@end

Swift 高逼格版本那个高手会,教一下子呗 QQ 2934525789  微信 18258182915                 也可以进QQ群 769718001  一起交流一下iOS开发

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容