美文网首页iOS程序猿iOS DeveloperiOS学习开发
iOS开发-带Placeholder的UITextView实现

iOS开发-带Placeholder的UITextView实现

作者: 施主小欣 | 来源:发表于2016-11-14 10:42 被阅读2920次

    刚从事这个行业的时候,想在UITextView上加Placeholder这个功能上为难了一下,然后各种查,也在他人的博客、简书上留言来寻找答案。但是实现起来都差强人意。后来偶然的情况下找到了封装好的类簇,用起来非常方便。最近有挺多新手也遇到这样的问题,通过我之前在他人博客上留的邮箱老找到我的QQ询问我是如何解决的。我想想还是把他放在网络上比较好。

    UITextView+Placeholder.h
    #import <UIKit/UIKit.h>

    @interface UITextView (Placeholder)
    
    @property (nonatomic, readonly) UILabel *placeholderLabel;
    
    @property (nonatomic, strong) NSString *placeholder;
    @property (nonatomic, strong) NSAttributedString *attributedPlaceholder;
    @property (nonatomic, strong) UIColor *placeholderColor;
    
    + (UIColor *)defaultPlaceholderColor;
    
    @end
    

    UITextView+Placeholder.m

    #import <objc/runtime.h>
    #import "UITextView+Placeholder.h"
    
    @implementation UITextView (Placeholder)
    
    #pragma mark - Swizzle Dealloc
    
    + (void)load {
    // is this the best solution?
    method_exchangeImplementations(class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc")),
                                   class_getInstanceMethod(self.class, @selector(swizzledDealloc)));
    }
    
    - (void)swizzledDealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    UILabel *label = objc_getAssociatedObject(self, @selector(placeholderLabel));
    if (label) {
        for (NSString *key in self.class.observingKeys) {
            @try {
                [self removeObserver:self forKeyPath:key];
            }
            @catch (NSException *exception) {
                // Do nothing
            }
        }
    }
    [self swizzledDealloc];
    }
    
    #pragma mark - Class Methods
    #pragma mark `defaultPlaceholderColor`
    
    + (UIColor *)defaultPlaceholderColor {
    static UIColor *color = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        UITextField *textField = [[UITextField alloc] init];
        textField.placeholder = @" ";
        color = [textField valueForKeyPath:@"_placeholderLabel.textColor"];
    });
    return color;
    }
    
    #pragma mark - `observingKeys`
    
    + (NSArray *)observingKeys {
    return @[@"attributedText",
             @"bounds",
             @"font",
             @"frame",
             @"text",
             @"textAlignment",
             @"textContainerInset"];
    }
    
    #pragma mark - Properties
    #pragma mark `placeholderLabel`
    
    - (UILabel *)placeholderLabel {
    UILabel *label = objc_getAssociatedObject(self, @selector(placeholderLabel));
    if (!label) {
        NSAttributedString *originalText = self.attributedText;
        self.text = @" "; // lazily set font of `UITextView`.
        self.attributedText = originalText;
    
        label = [[UILabel alloc] init];
        label.textColor = [self.class defaultPlaceholderColor];
        label.numberOfLines = 0;
        label.userInteractionEnabled = NO;
        objc_setAssociatedObject(self, @selector(placeholderLabel), label, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(updatePlaceholderLabel)
                                                     name:UITextViewTextDidChangeNotification
                                                   object:self];
    
        for (NSString *key in self.class.observingKeys) {
    
            [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
        }
    }
      return label;
    }
    
    #pragma mark `placeholder`
    
    - (NSString *)placeholder {
        return self.placeholderLabel.text;
    }
    
    - (void)setPlaceholder:(NSString *)placeholder {
    
        self.placeholderLabel.text = placeholder;
        [self updatePlaceholderLabel];
    }
    
    - (NSAttributedString *)attributedPlaceholder {
    return self.placeholderLabel.attributedText;
    }
    
    - (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder {
    self.placeholderLabel.attributedText = attributedPlaceholder;
    [self updatePlaceholderLabel];
    }
    
    #pragma mark `placeholderColor`
    
    - (UIColor *)placeholderColor {
    return self.placeholderLabel.textColor;
    }
    
    - (void)setPlaceholderColor:(UIColor *)placeholderColor {
    self.placeholderLabel.textColor = placeholderColor;
    }
    #pragma mark - KVO
    
    - (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    [self updatePlaceholderLabel];
    }
    
    #pragma mark - Update
    
    - (void)updatePlaceholderLabel {
    if (self.text.length) {
        [self.placeholderLabel removeFromSuperview];
        return;
    }
    
    [self insertSubview:self.placeholderLabel atIndex:0];
    
    self.placeholderLabel.font = self.font;
    self.placeholderLabel.textAlignment = self.textAlignment;
    
    // `NSTextContainer` is available since iOS 7
    CGFloat lineFragmentPadding;
    UIEdgeInsets textContainerInset;
    
    #pragma deploymate push "ignored-api-availability"
    // iOS 7+
    if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {
        lineFragmentPadding = self.textContainer.lineFragmentPadding;
        textContainerInset = self.textContainerInset;
    }
    #pragma deploymate pop
    
    // iOS 6
    else {
        lineFragmentPadding = 5;
        textContainerInset = UIEdgeInsetsMake(8, 0, 8, 0);
    }
    
    CGFloat x = lineFragmentPadding + textContainerInset.left;
    CGFloat y = textContainerInset.top;
    CGFloat width = CGRectGetWidth(self.bounds) - x - lineFragmentPadding - textContainerInset.right;
    CGFloat height = [self.placeholderLabel sizeThatFits:CGSizeMake(width, 0)].height;
    self.placeholderLabel.frame = CGRectMake(x, y, width, height);
    }
    
    @end
    

    iOS中UITextField带有PlaceHolder属性,可以方便用于提示输入。但是同样可以进行文本输入的UITextView控件则没有PlaceHolder属性,还是有些不方便的。
    使用方法:把文件引入工程中然后直接用属性placeHolder就可以实现效果。

    核心思路就是使用2个UITextView来模拟PlaceHolder的效果,其中做为输入区域的TextView在表面,背景要设为透明,作为PlaceHolder角色的TextView则在底层,两者通过UITextViewDelegate来动态控制。

    相关文章

      网友评论

        本文标题:iOS开发-带Placeholder的UITextView实现

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