// // YMTextView.m // andaowei // // Created by YoMi on 2023/11/6. // #import "YMTextView.h" @interface YMTextView () @property (nonatomic, copy) YMTextViewHandler beginEditingHandler; ///< 文本开始编辑Block @property (nonatomic, copy) YMTextViewHandler changeHandler; ///< 文本改变Block @property (nonatomic, copy) YMTextViewHandler endEditingHandler; ///< 文本结束编辑Block @property (nonatomic, copy) YMTextViewHandler maxHandler; ///< 达到最大限制字符数Block @property (nonatomic, strong) UITextView *placeholderTextView; ///< placeholderTextView @property (nonatomic, strong) NSMutableArray *placeholderTextViewConstraints; /// 0 && (self.maxHeight >= self.bounds.size.height || self.maxHeight == 0)) { if (self.maxHeight == 0) { self.maxHeight = MAXFLOAT; } // 计算高度 NSInteger currentHeight = ceil([self sizeThatFits:CGSizeMake(self.bounds.size.width, MAXFLOAT)].height); // 如果高度有变化,调用block if (currentHeight != self.lastHeight) { // 是否可以滚动 self.scrollEnabled = currentHeight >= self.maxHeight; CGFloat currentTextViewHeight = currentHeight >= self.maxHeight ? self.maxHeight : currentHeight; // 改变textView的高度 if (currentTextViewHeight < self.minHeight) currentTextViewHeight = self.minHeight; CGRect frame = self.frame; frame.size.height = currentTextViewHeight; self.frame = frame; // 调用block dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIView performWithoutAnimation:^{ if (self.textViewHeightDidChanged) self.textViewHeightDidChanged(self, currentTextViewHeight); }]; }); // 记录当前高度 self.lastHeight = currentTextViewHeight; } }else { self.needTextViewHeightChanged = YES; } } #pragma mark - Getter /// 返回一个经过处理的 `self.text` 的值, 去除了首位的空格和换行. - (NSString *)formatText { return [[super text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; // 去除首尾的空格和换行. } #pragma mark - Setter - (void)setText:(NSString *)text { [super setText:text]; self.placeholderTextView.hidden = [@(text.length) boolValue]; // 手动模拟触发通知 NSNotification *notification = [NSNotification notificationWithName:UITextViewTextDidChangeNotification object:self]; [self textDidChange:notification]; } - (void)setFont:(UIFont *)font { [super setFont:font]; self.placeholderTextView.font = font; } - (void)setMaxLength:(NSUInteger)maxLength { _maxLength = fmax(0, maxLength); self.text = self.text; } - (void)setCornerRadius:(CGFloat)cornerRadius { _cornerRadius = cornerRadius; self.layer.cornerRadius = _cornerRadius; } - (void)setBorderColor:(UIColor *)borderColor { if (!borderColor) return; _borderColor = borderColor; self.layer.borderColor = _borderColor.CGColor; } - (void)setBorderWidth:(CGFloat)borderWidth { _borderWidth = borderWidth; self.layer.borderWidth = _borderWidth; } - (void)setPlaceholder:(NSString *)placeholder { if (!placeholder) return; _placeholder = [placeholder copy]; if (_placeholder.length > 0) { self.placeholderTextView.text = _placeholder; } } - (void)setPlaceholderColor:(UIColor *)placeholderColor { if (!placeholderColor) return; _placeholderColor = placeholderColor; self.placeholderTextView.textColor = _placeholderColor; } - (void)setPlaceholderFont:(UIFont *)placeholderFont { if (!placeholderFont) return; _placeholderFont = placeholderFont; self.placeholderTextView.font = _placeholderFont; } - (void)setPlaceholderMaximumNumberOfLines:(NSInteger)placeholderMaximumNumberOfLines { _placeholderMaximumNumberOfLines = placeholderMaximumNumberOfLines; self.placeholderTextView.textContainer.maximumNumberOfLines = placeholderMaximumNumberOfLines; } - (void)setPlaceholderLineBreakMode:(NSLineBreakMode)placeholderLineBreakMode { _placeholderLineBreakMode = placeholderLineBreakMode; self.placeholderTextView.textContainer.lineBreakMode = placeholderLineBreakMode; } #pragma mark - NSNotification - (void)textDidBeginEditing:(NSNotification *)notification { if (!CGPointEqualToPoint(self.placeholderTextView.contentOffset, CGPointZero)) { CGSize contentSize = self.placeholderTextView.contentSize; self.placeholderTextView.contentSize = CGSizeZero; self.placeholderTextView.contentOffset = CGPointZero; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ self.placeholderTextView.contentSize = contentSize; }); } !_beginEditingHandler ?: _beginEditingHandler(self); } - (void)textDidChange:(NSNotification *)notification { // 通知回调的实例的不是当前实例的话直接返回 if (notification.object != self) return; // 根据字符数量显示或者隐藏 `placeholderLabel` self.placeholderTextView.hidden = [@(self.text.length) boolValue]; self.placeholderTextView.contentOffset = CGPointZero; // 禁止第一个字符输入换行 while ([self.text hasPrefix:@"\n"]) { self.text = [self.text substringFromIndex:1]; } // 禁止第一个字符输入空格 while (self.disableFirstWhitespace && [self.text hasPrefix:@" "]) { self.text = [self.text substringFromIndex:1]; } if (self.disableWhitespace && [self.text containsString:@" "]) { self.text = [self.text stringByReplacingOccurrencesOfString:@" " withString:@""]; } if ([self.text containsString:@"\n"]) { if (self.disableNewline) { self.text = [self.text stringByReplacingOccurrencesOfString:@"\n" withString:@""]; } if (self.isResignFirstResponderAfterReturn) { [self resignFirstResponder]; } } // 只有当maxLength字段的值不为无穷大整型也不为0时才计算限制字符数. if (_maxLength != NSUIntegerMax && _maxLength != 0 && self.text.length > 0) { if (!self.markedTextRange && self.text.length > _maxLength) { !_maxHandler ?: _maxHandler(self); // 回调达到最大限制的Block. self.text = [self.text substringToIndex:_maxLength]; // 截取最大限制字符数. [self.undoManager removeAllActions]; // 达到最大字符数后清空所有 undoaction, 以免 undo 操作造成crash. } } // 回调文本改变的Block. !_changeHandler ?: _changeHandler(self); [self textViewHeightDidChangedCalculate]; } - (void)textDidEndEditing:(NSNotification *)notification { !_endEditingHandler ?: _endEditingHandler(self); } #pragma mark - Public + (instancetype)textView { return [[self alloc] init]; } - (void)addTextDidBeginEditingHandler:(YMTextViewHandler)beginEditingHandler { _beginEditingHandler = [beginEditingHandler copy]; } - (void)addTextDidChangeHandler:(YMTextViewHandler)changeHandler { _changeHandler = [changeHandler copy]; } - (void)addTextDidEndEditingHandler:(YMTextViewHandler)endEditingHandler { _endEditingHandler = [endEditingHandler copy]; } - (void)addTextLengthDidMaxHandler:(YMTextViewHandler)maxHandler { _maxHandler = [maxHandler copy]; } @end