苏飞论坛

 找回密码
 马上注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

分布式系统框架(V1.3) 轻松承载百亿数据,千万流量!讨论专区 - 源码下载 - 官方教程

HttpHelper万能框架(V2.3) HttpHelper官方出品,无敌框架讨论区 - 源码下载 - 在线测试和代码生成

HttpHelper爬虫类(V1.9) 最牛的爬虫类,没有爬不到只有想不到 源码 - 代码生成器 - 讨论区 - 教程- 例子

查看: 116|回复: 6

[相关工具] UITextView优雅实现Placeholder、字数、行数限制

[复制链接]
  • TA的每日心情
    奋斗
    昨天 09:37
  • 签到天数: 21 天

    [LV.4]偶尔看看III

    发表于 2018-12-3 16:15:32 | 显示全部楼层 |阅读模式
    本帖最后由 竹林风 于 2018-12-3 16:33 编辑

    对于UITextView是没有Placeholder 这个属性的,我也用过很多第三方的封装类来实现这个功能,但总觉得繁琐,不舒服。在此用runtime写了一个分类,外加字数和行数限制,可根据自己的需求分别设置。

    先看一下效果吧

    Untitled.gif

    下面是具体实现:

    先新建一个分类 UITextView (ZLTextView)

    1D966462419ECB8960A9152CC18B6474.jpg
    选择 Objective-C File
    68AF5B4C68BC0AC2D3E8C88F13342ABC.png

    File 是分类的名字 FileType 选择 Category Class 选择UITextView

    D98D1B79D98BD8E43C89CA359118374B.png

    .h文件属性

    [Objective-C] 纯文本查看 复制代码
    #import <UIKit/UIKit.h>
    
    @interface UITextView (ZLTextView)
    
    //对placehoulder和limitLength的赋值要在textView.text 之后赋值
    
    @property (nonatomic,strong) NSString *placeholder;//占位符,可与下面属性任意一个同时设置
    
    @property (nonatomic,  copy) NSNumber *limitLength;//需要限制的字数          优先级高于lines
    
    @property (nonatomic,  copy) NSNumber *limitLines;//需要限制的行数
    
    @property(nonatomic, strong) UIFont   *placeholdFont;//占位符文字          默认13号字体
    
    @property(nonatomic, strong) UIFont   *limitPlaceFont;//行数、字数限制文字     默认13号字体
    
    @property(nonatomic, strong) UIColor  *placeholdColor;//占位符文字颜色    默认灰色
    
    @property(nonatomic, strong) UIColor  *limitPlaceColor;//行数、字数限制文字颜色 默认灰色
    
    @property(nonatomic,   copy) NSNumber *autoHeight;//自动高度 默认不计算 设置@1自动计算高度
    
    @end


    .m文件实现代码

    [Objective-C] 纯文本查看 复制代码
    #import "UITextView+ZLTextView.h"
    #import <objc/runtime.h>
    @interface UITextView ()
    @property (nonatomic,strong) UILabel *placeholderLabel;//占位符
    @property (nonatomic,strong) UILabel *wordCountLabel;//计算字数
    @property (nonatomic,strong) NSValue *oldFrame;//记录初始frame
    
    @end
    
    @implementation UITextView (ZLTextView)
    
    static NSString *PLACEHOLDLABEL = @"placelabel";
    static NSString *PLACEHOLD = @"placehold";
    static NSString *WORDCOUNTLABEL = @"wordcount";
    static NSString *PLACEHOLDFONT = @"placeholdfont";
    static NSString *LIMITFONT = @"limitfont";
    static NSString *PLACEHOLDCOLOR = @"placeholdcolor";
    static NSString *LIMITCOLOR = @"limitcolor";
    static NSString *AUTOHEIGHT = @"autoheight";
    static NSString *OLDFRAME = @"oldframe";
    static const void *limitLengthKey = &limitLengthKey;
    static const void *limitLinesKey = &limitLinesKey;
    
    #pragma mark -- set/get...
    
    - (void)setPlaceholderLabel:(UILabel *)placeholderLabel {
        objc_setAssociatedObject(self, &PLACEHOLDLABEL, placeholderLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (UILabel *)placeholderLabel {
        return objc_getAssociatedObject(self, &PLACEHOLDLABEL);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (void)setPlaceholder:(NSString *)placeholder {
        objc_setAssociatedObject(self, &PLACEHOLD, placeholder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        [self setPlaceHolderLabel:placeholder];
    }
    - (NSString *)placeholder {
        return objc_getAssociatedObject(self, &PLACEHOLD);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (UILabel *)wordCountLabel {
        return objc_getAssociatedObject(self, &WORDCOUNTLABEL);
    }
    - (void)setWordCountLabel:(UILabel *)wordCountLabel {
        objc_setAssociatedObject(self, &WORDCOUNTLABEL, wordCountLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (NSNumber *)limitLength {
        return objc_getAssociatedObject(self, limitLengthKey);
    }
    - (void)setLimitLength:(NSNumber *)limitLength {
        objc_setAssociatedObject(self, limitLengthKey, limitLength, OBJC_ASSOCIATION_COPY_NONATOMIC);
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewChanged:) name:UITextViewTextDidChangeNotification object:self];
        [self setWordcountLable:limitLength];
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (NSNumber *)limitLines {
        return objc_getAssociatedObject(self, limitLinesKey);
    }
    - (void)setLimitLines:(NSNumber *)limitLines {
        objc_setAssociatedObject(self, limitLinesKey, limitLines, OBJC_ASSOCIATION_COPY_NONATOMIC);
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewChanged:) name:UITextViewTextDidChangeNotification object:self];
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (void)setLimitPlaceFont:(UIFont *)limitPlaceFont {
        if (self.wordCountLabel) {
            self.wordCountLabel.font = limitPlaceFont;
        }
        objc_setAssociatedObject(self, &LIMITFONT, limitPlaceFont, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (UIFont *)limitPlaceFont {
        return objc_getAssociatedObject(self, &LIMITFONT) == nil ? [UIFont systemFontOfSize:13.] : objc_getAssociatedObject(self, &LIMITFONT);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (void)setPlaceholdFont:(UIFont *)placeholdFont {
        if (self.placeholderLabel) {
            self.placeholderLabel.font = placeholdFont;
        }
        objc_setAssociatedObject(self, &PLACEHOLDFONT, placeholdFont, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (UIFont *)placeholdFont {
        return objc_getAssociatedObject(self, &PLACEHOLDFONT) == nil ? [UIFont systemFontOfSize:13.] : objc_getAssociatedObject(self, &PLACEHOLDFONT);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (void)setPlaceholdColor:(UIColor *)placeholdColor {
        if (self.placeholderLabel) {
            self.placeholderLabel.textColor = placeholdColor;
        }
        objc_setAssociatedObject(self, &PLACEHOLDCOLOR, placeholdColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (UIColor *)placeholdColor {
        return objc_getAssociatedObject(self, &PLACEHOLDCOLOR) == nil ? [UIColor lightGrayColor] : objc_getAssociatedObject(self, &PLACEHOLDCOLOR);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (void)setLimitPlaceColor:(UIColor *)limitPlaceColor {
        if (self.wordCountLabel) {
            self.wordCountLabel.textColor = limitPlaceColor;
        }
        objc_setAssociatedObject(self, &LIMITCOLOR, limitPlaceColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (UIColor *)limitPlaceColor {
        return objc_getAssociatedObject(self, &LIMITCOLOR) == nil ? [UIColor lightGrayColor] : objc_getAssociatedObject(self, &LIMITCOLOR);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (void)setAutoHeight:(NSNumber *)autoHeight {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewChanged:) name:UITextViewTextDidChangeNotification object:self];
        objc_setAssociatedObject(self, &AUTOHEIGHT, autoHeight, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    - (NSNumber *)autoHeight {
        return objc_getAssociatedObject(self, &AUTOHEIGHT);
    }
    
    //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    - (void)setOldFrame:(NSValue *)oldFrame {
        objc_setAssociatedObject(self, &OLDFRAME, oldFrame, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    - (NSValue *)oldFrame {
        return objc_getAssociatedObject(self, &OLDFRAME);
    }
    #pragma mark -- 配置占位符标签
    
    - (void)setPlaceHolderLabel:(NSString *)placeholder {
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewChanged:) name:UITextViewTextDidChangeNotification object:self];
        if (self.placeholderLabel) {
            [self.placeholderLabel removeFromSuperview];
        }
        /*
         *  占位字符
         */
        self.placeholderLabel = [[UILabel alloc] init];
        self.placeholderLabel.font = self.placeholdFont;
        self.placeholderLabel.text = placeholder;
        self.placeholderLabel.numberOfLines = 0;
        self.placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping;
        self.placeholderLabel.textColor = self.placeholdColor;
        CGRect rect = [placeholder boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.frame)-7, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: self.placeholdFont} context:nil];
        self.placeholderLabel.frame = CGRectMake(7, 7, rect.size.width, rect.size.height);
        [self addSubview:self.placeholderLabel];
        self.placeholderLabel.hidden = self.text.length > 0 ? YES : NO;
        self.wordCountLabel.text = [NSString stringWithFormat:@"%lu/%@",(unsigned long)self.text.length,self.limitLength];
        
    }
    
    #pragma mark -- 配置字数限制标签
    
    - (void)setWordcountLable:(NSNumber *)limitLength {
        if (self.wordCountLabel) {
            [self.wordCountLabel removeFromSuperview];
        }
        /*
         *  字数限制
         */
        self.wordCountLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(self.frame) - 20, CGRectGetWidth(self.frame) - 10, 20)];
        self.wordCountLabel.textAlignment = NSTextAlignmentRight;
        self.wordCountLabel.textColor = self.limitPlaceColor;
        self.wordCountLabel.font = self.limitPlaceFont;
        if (self.text.length > [limitLength integerValue]) {
            self.text = [self.text substringToIndex:[self.limitLength intValue]];
        }
        self.wordCountLabel.text = [NSString stringWithFormat:@"%lu/%@",(unsigned long)self.text.length,limitLength];
        [self addSubview:self.wordCountLabel];
        
        self.contentInset = UIEdgeInsetsMake(0, 0, 20, 0);
    }
    
    #pragma mark -- NSNotification
    
    - (void)textViewChanged:(NSNotification *)notification {
        NSInteger wordCount = self.text.length;
        
        if (self.placeholder) {
            self.placeholderLabel.hidden = YES;
            if (wordCount == 0) {
                self.placeholderLabel.hidden = NO;
            }
        }
        if (self.limitLength) {//字数限制
            if (wordCount > [self.limitLength integerValue]) {
                wordCount = [self.limitLength integerValue];
            }
            NSString *keyboardType = self.textInputMode.primaryLanguage;
            if ([keyboardType isEqualToString:@"zh-Hans"]) {//对简体中文做特殊处理>>>>高亮拼写问题
                UITextRange *range = [self markedTextRange];
                if (!range) {
                    if (self.text.length > [self.limitLength intValue]) {
                        self.text = [self.text substringToIndex:[self.limitLength intValue]];
                        NSLog(@"已经是最大字数");
                    }else {/*有高亮不做限制*/}
                }
            }else {
                if ([self.text length] > [self.limitLength intValue]) {
                    self.text = [self.text substringToIndex:[self.limitLength intValue]];
                    NSLog(@"已经是最大字数");
                }
            }
            self.wordCountLabel.text = [NSString stringWithFormat:@"%ld/%@",(long)wordCount,self.limitLength];
        }else {
            if (self.limitLines) {//行数限制
                CGSize size = [self getStringPlaceSize:self.text textFont:self.font bundingSize:CGSizeMake(self.contentSize.width-10, CGFLOAT_MAX)];
                if (size.height > self.font.lineHeight * [self.limitLines intValue]) {
                    self.text = [self.text substringToIndex:self.text.length - 1];
                    NSLog(@"已经是最大行数/n行数限制,没有做右下角label提示,若有此需求,联系我");
                }
                /*行数的限制,没有做右下角提示*/
            }
        }
        if (self.autoHeight) {
            CGSize size = [self getStringPlaceSize:self.text textFont:self.font bundingSize:CGSizeMake(CGRectGetWidth(self.frame), MAXFLOAT)];
            CGRect oldRect = self.oldFrame.CGRectValue;
            [UIView animateWithDuration:0.15 animations:^{
                self.frame = CGRectMake(oldRect.origin.x, oldRect.origin.y, oldRect.size.width, size.height + 25 <= oldRect.size.height ? oldRect.size.height : size.height + 25);
            }];
        }
        
    }
    
    - (CGSize)getStringPlaceSize:(NSString *)string textFont:(UIFont *)font bundingSize:(CGSize)boundSize {
        //计算文本高度
        NSDictionary *attribute = @{NSFontAttributeName:font};
        NSStringDrawingOptions option = (NSStringDrawingOptions)(NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading);
        CGSize size = [string boundingRectWithSize:boundSize options:option attributes:attribute context:nil].size;
        return size;
    }
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextViewTextDidChangeNotification object:self];
    }
    - (void)layoutSubviews {
        [super layoutSubviews];
        if (self.limitLength && self.wordCountLabel) {
            /*
             *  避免外部使用了约束 这里再次更新frame
             */
            self.wordCountLabel.frame = CGRectMake(0, CGRectGetHeight(self.frame) - 20 + self.contentOffset.y, CGRectGetWidth(self.frame) - 10, 20);
        }
        if (self.placeholder && self.placeholderLabel) {
            CGRect rect = [self.placeholder boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.frame)-7, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: self.placeholdFont} context:nil];
            self.placeholderLabel.frame = CGRectMake(7, 7, rect.size.width, rect.size.height);
        }
        if ([self.autoHeight isEqual:@1]) {
            CGRect currentFrame = self.frame;
            self.oldFrame = [NSValue valueWithCGRect:currentFrame];
        }
    }
    


    具体使用:

    在需要使用的类中引用 #import "UITextView+ZLTextView.h"

    [Objective-C] 纯文本查看 复制代码
    UITextView *textView = [[UITextView alloc] init];
        textView.frame = CGRectMake(60, 100, 240, 120);
        textView.placeholder = @"我是一个占位符";
    //    textView.limitLength = @10;
        textView.placeholdColor = [UIColor lightGrayColor];
        textView.limitPlaceColor = [UIColor blueColor];
        textView.placeholdFont = [UIFont systemFontOfSize:14];
        textView.limitPlaceFont = [UIFont systemFontOfSize:17];
        //    textView.limitLines = @4;//行数限制优先级低于字数限制
        [self.view addSubview:textView];


    附件: Test-ZLTextView.zip (106.63 KB, 下载次数: 0)
  • TA的每日心情
    奋斗
    昨天 09:37
  • 签到天数: 21 天

    [LV.4]偶尔看看III

     楼主| 发表于 2018-12-3 16:15:33 | 显示全部楼层
  • TA的每日心情
    开心
    昨天 12:22
  • 签到天数: 20 天

    [LV.4]偶尔看看III

    发表于 2018-12-3 16:16:11 | 显示全部楼层
  • TA的每日心情
    开心
    昨天 09:03
  • 签到天数: 17 天

    [LV.4]偶尔看看III

    发表于 2018-12-3 16:23:44 | 显示全部楼层
  • TA的每日心情
    开心
    昨天 09:05
  • 签到天数: 643 天

    [LV.9]以坛为家II

    发表于 2018-12-3 17:06:27 | 显示全部楼层
  • TA的每日心情
    慵懒
    昨天 08:50
  • 签到天数: 19 天

    [LV.4]偶尔看看III

    发表于 2018-12-3 17:25:45 | 显示全部楼层
  • TA的每日心情
    奋斗
    昨天 08:44
  • 签到天数: 199 天

    [LV.7]常住居民III

    发表于 2018-12-3 20:33:01 | 显示全部楼层
    您需要登录后才可以回帖 登录 | 马上注册

    本版积分规则

    QQ|手机版|小黑屋|手机版|联系我们|关于我们|广告合作|苏飞论坛 ( 豫ICP备17001017号-1 )

    GMT+8, 2018-12-13 01:30

    © 2017-2018

    快速回复 返回顶部 返回列表