美文网首页
iOS 利用runtime给UITextView加上占位符pl

iOS 利用runtime给UITextView加上占位符pl

作者: Cary9396 | 来源:发表于2018-08-29 11:04 被阅读0次

    我们都知道UITextField有个placeholder属性来展示没有文字输入时的占位符,可是UITextView却没有,偏偏这个确实我们平常开发经常会遇到的需求。当然解决方式多种多样,今天我们来说一种通过runtime添加属性的方式来实现。
    我们首先给UITetxView创建一个类别Category,然后利用runtime来添加属性。代码如下:
    首先 .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
    

    .m文件:

    #import "UITextView+Placeholder.h"
    #import <objc/runtime.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
    
    

    使用的时候导入 #import "UITextView+Placeholder.h"

        _textView = [[UITextView alloc]init];
        _textView.placeholder = @"占位符显示内容";
        _textView.placeholderColor = [UIColor colorWithHexString:@"#999999"];
        _textView.placeholderLabel.font = [UIFont systemFontOfSize:15];
        _textView.font = [UIFont systemFontOfSize:15];
        _textView.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 10);
        [self.view addSubview:_textView];
       [_textView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.view.mas_left).offset(0);
            make.top.equalTo(bg.mas_bottom).offset(0);
            make.width.mas_equalTo(xScreenWidth);
            make.height.mas_equalTo(170);
        }];
    

    ok~~,完美解决问题。代码可以直接粘过去用。。。。

    相关文章

      网友评论

          本文标题:iOS 利用runtime给UITextView加上占位符pl

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