| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- //
- // UITextView+WZB.m
- // WZBTextView-demo
- //
- // Created by normal on 2016/11/14.
- // Copyright © 2016年 onecloud.ltd. All rights reserved.
- //
- #import "UITextView+WZB.h"
- #import <objc/runtime.h>
- // 占位文字
- static const void *WZBPlaceholderViewKey = &WZBPlaceholderViewKey;
- // 占位文字颜色
- static const void *WZBPlaceholderColorKey = &WZBPlaceholderColorKey;
- // 最大高度
- static const void *WZBTextViewMaxHeightKey = &WZBTextViewMaxHeightKey;
- // 最小高度
- static const void *WZBTextViewMinHeightKey = &WZBTextViewMinHeightKey;
- // 高度变化的block
- static const void *WZBTextViewHeightDidChangedBlockKey = &WZBTextViewHeightDidChangedBlockKey;
- // 存储添加的图片
- static const void *WZBTextViewImageArrayKey = &WZBTextViewImageArrayKey;
- // 存储最后一次改变高度后的值
- static const void *WZBTextViewLastHeightKey = &WZBTextViewLastHeightKey;
- @interface UITextView ()
- // 存储添加的图片
- @property (nonatomic, strong) NSMutableArray *wzb_imageArray;
- // 存储最后一次改变高度后的值
- @property (nonatomic, assign) CGFloat lastHeight;
- @end
- @implementation UITextView (WZB)
- #pragma mark - Swizzle Dealloc
- + (void)load {
- // 交换dealoc
- Method dealoc = class_getInstanceMethod(self.class, NSSelectorFromString(@"dealloc"));
- Method myDealloc = class_getInstanceMethod(self.class, @selector(myDealloc));
- method_exchangeImplementations(dealoc, myDealloc);
- }
- - (void)myDealloc {
- // 移除监听
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- UITextView *placeholderView = objc_getAssociatedObject(self, WZBPlaceholderViewKey);
-
- // 如果有值才去调用,这步很重要
- if (placeholderView) {
- NSArray *propertys = @[@"frame", @"bounds", @"font", @"text", @"textAlignment", @"textContainerInset"];
- for (NSString *property in propertys) {
- @try {
- [self removeObserver:self forKeyPath:property];
- } @catch (NSException *exception) {}
- }
- }
- [self myDealloc];
- }
- #pragma mark - set && get
- - (UITextView *)wzb_placeholderView {
-
- // 为了让占位文字和textView的实际文字位置能够完全一致,这里用UITextView
- UITextView *placeholderView = objc_getAssociatedObject(self, WZBPlaceholderViewKey);
-
- if (!placeholderView) {
-
- // 初始化数组
- self.wzb_imageArray = [NSMutableArray array];
-
- placeholderView = [[UITextView alloc] init];
- // 动态添加属性的本质是: 让对象的某个属性与值产生关联
- objc_setAssociatedObject(self, WZBPlaceholderViewKey, placeholderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- placeholderView = placeholderView;
-
- // 设置基本属性
- placeholderView.scrollEnabled = placeholderView.userInteractionEnabled = NO;
- // self.scrollEnabled = placeholderView.scrollEnabled = placeholderView.showsHorizontalScrollIndicator = placeholderView.showsVerticalScrollIndicator = placeholderView.userInteractionEnabled = NO;
- placeholderView.textColor = [UIColor lightGrayColor];
- placeholderView.backgroundColor = [UIColor clearColor];
- [self refreshPlaceholderView];
- [self addSubview:placeholderView];
-
- // 监听文字改变
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewTextChange) name:UITextViewTextDidChangeNotification object:self];
-
- // 这些属性改变时,都要作出一定的改变,尽管已经监听了TextDidChange的通知,也要监听text属性,因为通知监听不到setText:
- NSArray *propertys = @[@"frame", @"bounds", @"font", @"text", @"textAlignment", @"textContainerInset"];
-
- // 监听属性
- for (NSString *property in propertys) {
- [self addObserver:self forKeyPath:property options:NSKeyValueObservingOptionNew context:nil];
- }
-
- }
- return placeholderView;
- }
- - (void)setWzb_placeholder:(NSString *)placeholder
- {
- // 为placeholder赋值
- [self wzb_placeholderView].text = placeholder;
- }
- - (NSString *)wzb_placeholder
- {
- // 如果有placeholder值才去调用,这步很重要
- if (self.placeholderExist) {
- return [self wzb_placeholderView].text;
- }
- return nil;
- }
- - (void)setWzb_placeholderColor:(UIColor *)wzb_placeholderColor
- {
- // 如果有placeholder值才去调用,这步很重要
- if (!self.placeholderExist) {
- MOLogV(@"请先设置placeholder值!");
- } else {
- self.wzb_placeholderView.textColor = wzb_placeholderColor;
-
- // 动态添加属性的本质是: 让对象的某个属性与值产生关联
- objc_setAssociatedObject(self, WZBPlaceholderColorKey, wzb_placeholderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- }
- - (UIColor *)wzb_placeholderColor
- {
- return objc_getAssociatedObject(self, WZBPlaceholderColorKey);
- }
- - (void)setWzb_maxHeight:(CGFloat)wzb_maxHeight
- {
- CGFloat max = wzb_maxHeight;
-
- // 如果传入的最大高度小于textView本身的高度,则让最大高度等于本身高度
- if (wzb_maxHeight < self.frame.size.height) {
- max = self.frame.size.height;
- }
-
- objc_setAssociatedObject(self, WZBTextViewMaxHeightKey, [NSString stringWithFormat:@"%lf", max], OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (CGFloat)wzb_maxHeight
- {
- return [objc_getAssociatedObject(self, WZBTextViewMaxHeightKey) doubleValue];
- }
- - (void)setWzb_minHeight:(CGFloat)wzb_minHeight
- {
- objc_setAssociatedObject(self, WZBTextViewMinHeightKey, [NSString stringWithFormat:@"%lf", wzb_minHeight], OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (CGFloat)wzb_minHeight
- {
- return [objc_getAssociatedObject(self, WZBTextViewMinHeightKey) doubleValue];
- }
- - (void)setWzb_textViewHeightDidChanged:(textViewHeightDidChangedBlock)wzb_textViewHeightDidChanged
- {
- objc_setAssociatedObject(self, WZBTextViewHeightDidChangedBlockKey, wzb_textViewHeightDidChanged, OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (textViewHeightDidChangedBlock)wzb_textViewHeightDidChanged
- {
- void(^textViewHeightDidChanged)(CGFloat currentHeight) = objc_getAssociatedObject(self, WZBTextViewHeightDidChangedBlockKey);
- return textViewHeightDidChanged;
- }
- - (NSArray *)wzb_getImages
- {
- return self.wzb_imageArray;
- }
- - (void)setLastHeight:(CGFloat)lastHeight {
- objc_setAssociatedObject(self, WZBTextViewLastHeightKey, [NSString stringWithFormat:@"%lf", lastHeight], OBJC_ASSOCIATION_COPY_NONATOMIC);
- }
- - (CGFloat)lastHeight {
- return [objc_getAssociatedObject(self, WZBTextViewLastHeightKey) doubleValue];
- }
- - (void)setWzb_imageArray:(NSMutableArray *)wzb_imageArray {
- objc_setAssociatedObject(self, WZBTextViewImageArrayKey, wzb_imageArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
- - (NSMutableArray *)wzb_imageArray {
- return objc_getAssociatedObject(self, WZBTextViewImageArrayKey);
- }
- - (void)wzb_autoHeightWithMaxHeight:(CGFloat)maxHeight
- {
- [self wzb_autoHeightWithMaxHeight:maxHeight textViewHeightDidChanged:nil];
- }
- // 是否启用自动高度,默认为NO
- static bool autoHeight = NO;
- - (void)wzb_autoHeightWithMaxHeight:(CGFloat)maxHeight textViewHeightDidChanged:(textViewHeightDidChangedBlock)textViewHeightDidChanged
- {
- autoHeight = YES;
- [self wzb_placeholderView];
- self.wzb_maxHeight = maxHeight;
- if (textViewHeightDidChanged) self.wzb_textViewHeightDidChanged = textViewHeightDidChanged;
- }
- #pragma mark - addImage
- /* 添加一张图片 */
- - (void)wzb_addImage:(UIImage *)image
- {
- [self wzb_addImage:image size:CGSizeZero];
- }
- /* 添加一张图片 image:要添加的图片 size:图片大小 */
- - (void)wzb_addImage:(UIImage *)image size:(CGSize)size
- {
- [self wzb_insertImage:image size:size index:self.attributedText.length > 0 ? self.attributedText.length : 0];
- }
- /* 插入一张图片 image:要添加的图片 size:图片大小 index:插入的位置 */
- - (void)wzb_insertImage:(UIImage *)image size:(CGSize)size index:(NSInteger)index
- {
- [self wzb_addImage:image size:size index:index multiple:-1];
- }
- /* 添加一张图片 image:要添加的图片 multiple:放大/缩小的倍数 */
- - (void)wzb_addImage:(UIImage *)image multiple:(CGFloat)multiple
- {
- [self wzb_addImage:image size:CGSizeZero index:self.attributedText.length > 0 ? self.attributedText.length : 0 multiple:multiple];
- }
- /* 插入一张图片 image:要添加的图片 multiple:放大/缩小的倍数 index:插入的位置 */
- - (void)wzb_insertImage:(UIImage *)image multiple:(CGFloat)multiple index:(NSInteger)index
- {
- [self wzb_addImage:image size:CGSizeZero index:index multiple:multiple];
- }
- /* 插入一张图片 image:要添加的图片 size:图片大小 index:插入的位置 multiple:放大/缩小的倍数 */
- - (void)wzb_addImage:(UIImage *)image size:(CGSize)size index:(NSInteger)index multiple:(CGFloat)multiple {
- if (image) [self.wzb_imageArray addObject:image];
- NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
- NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
- textAttachment.image = image;
- CGRect bounds = textAttachment.bounds;
- if (!CGSizeEqualToSize(size, CGSizeZero)) {
- bounds.size = size;
- textAttachment.bounds = bounds;
- } else if (multiple <= 0) {
- CGFloat oldWidth = textAttachment.image.size.width;
- CGFloat scaleFactor = oldWidth / (self.frame.size.width - 10);
- textAttachment.image = [UIImage imageWithCGImage:textAttachment.image.CGImage scale:scaleFactor orientation:UIImageOrientationUp];
- } else {
- bounds.size = image.size;
- textAttachment.bounds = bounds;
- }
-
- NSAttributedString *attrStringWithImage = [NSAttributedString attributedStringWithAttachment:textAttachment];
- [attributedString replaceCharactersInRange:NSMakeRange(index, 0) withAttributedString:attrStringWithImage];
- self.attributedText = attributedString;
- [self textViewTextChange];
- [self refreshPlaceholderView];
- }
- #pragma mark - KVO监听属性改变
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- [self refreshPlaceholderView];
- if ([keyPath isEqualToString:@"text"]) [self textViewTextChange];
- }
- // 刷新PlaceholderView
- - (void)refreshPlaceholderView {
-
- UITextView *placeholderView = objc_getAssociatedObject(self, WZBPlaceholderViewKey);
-
- // 如果有值才去调用,这步很重要
- if (placeholderView) {
- self.wzb_placeholderView.frame = self.bounds;
- if (self.wzb_maxHeight < self.bounds.size.height) self.wzb_maxHeight = self.bounds.size.height;
- self.wzb_placeholderView.font = self.font;
- self.wzb_placeholderView.textAlignment = self.textAlignment;
- self.wzb_placeholderView.textContainerInset = self.textContainerInset;
- self.wzb_placeholderView.hidden = (self.text.length > 0 && self.text);
- }
- }
- // 处理文字改变
- - (void)textViewTextChange {
- UITextView *placeholderView = objc_getAssociatedObject(self, WZBPlaceholderViewKey);
-
- // 如果有值才去调用,这步很重要
- if (placeholderView) {
- self.wzb_placeholderView.hidden = (self.text.length > 0 && self.text);
- }
- // 如果没有启用自动高度,不执行以下方法
- if (!autoHeight) return;
- if (self.wzb_maxHeight >= self.bounds.size.height) {
-
- // 计算高度
- NSInteger currentHeight = ceil([self sizeThatFits:CGSizeMake(self.bounds.size.width, MAXFLOAT)].height);
-
- // 如果高度有变化,调用block
- if (currentHeight != self.lastHeight) {
- // 是否可以滚动
- self.scrollEnabled = currentHeight >= self.wzb_maxHeight;
- CGFloat currentTextViewHeight = currentHeight >= self.wzb_maxHeight ? self.wzb_maxHeight : currentHeight;
- // 改变textView的高度
- if (currentTextViewHeight >= self.wzb_minHeight) {
- CGRect frame = self.frame;
- frame.size.height = currentTextViewHeight;
- self.frame = frame;
- // 调用block
- if (self.wzb_textViewHeightDidChanged) self.wzb_textViewHeightDidChanged(currentTextViewHeight);
- // 记录当前高度
- self.lastHeight = currentTextViewHeight;
- }
- }
- }
-
- if (!self.isFirstResponder) [self becomeFirstResponder];
- }
- // 判断是否有placeholder值,这步很重要
- - (BOOL)placeholderExist {
-
- // 获取对应属性的值
- UITextView *placeholderView = objc_getAssociatedObject(self, WZBPlaceholderViewKey);
-
- // 如果有placeholder值
- if (placeholderView) return YES;
-
- return NO;
- }
- #pragma mark - 过期
- - (NSString *)placeholder
- {
- return self.wzb_placeholder;
- }
- - (void)setPlaceholder:(NSString *)placeholder
- {
- self.wzb_placeholder = placeholder;
- }
- - (UIColor *)placeholderColor
- {
- return self.wzb_placeholderColor;
- }
- - (void)setPlaceholderColor:(UIColor *)placeholderColor
- {
- self.wzb_placeholderColor = placeholderColor;
- }
- - (void)setMaxHeight:(CGFloat)maxHeight
- {
- self.wzb_maxHeight = maxHeight;
- }
- - (CGFloat)maxHeight
- {
- return self.maxHeight;
- }
- - (void)setMinHeight:(CGFloat)minHeight
- {
- self.wzb_minHeight = minHeight;
- }
- - (CGFloat)minHeight
- {
- return self.wzb_minHeight;
- }
- - (void)setTextViewHeightDidChanged:(textViewHeightDidChangedBlock)textViewHeightDidChanged
- {
- self.wzb_textViewHeightDidChanged = textViewHeightDidChanged;
- }
- - (textViewHeightDidChangedBlock)textViewHeightDidChanged
- {
- return self.wzb_textViewHeightDidChanged;
- }
- - (NSArray *)getImages
- {
- return self.wzb_getImages;
- }
- - (void)autoHeightWithMaxHeight:(CGFloat)maxHeight
- {
- [self wzb_autoHeightWithMaxHeight:maxHeight];
- }
- - (void)autoHeightWithMaxHeight:(CGFloat)maxHeight textViewHeightDidChanged:(void(^)(CGFloat currentTextViewHeight))textViewHeightDidChanged
- {
- [self wzb_autoHeightWithMaxHeight:maxHeight textViewHeightDidChanged:textViewHeightDidChanged];
- }
- - (void)addImage:(UIImage *)image
- {
- [self wzb_addImage:image];
- }
- - (void)addImage:(UIImage *)image size:(CGSize)size
- {
- [self wzb_addImage:image size:size];
- }
- - (void)insertImage:(UIImage *)image size:(CGSize)size index:(NSInteger)index
- {
- [self wzb_insertImage:image size:size index:index];
- }
- - (void)addImage:(UIImage *)image multiple:(CGFloat)multiple
- {
- [self wzb_addImage:image multiple:multiple];
- }
- - (void)insertImage:(UIImage *)image multiple:(CGFloat)multiple index:(NSInteger)index
- {
- [self wzb_insertImage:image multiple:multiple index:index];
- }
- @end
|