// // HMEmoticonTextView.m // 表情键盘 // // Created by 刘凡 on 16/3/3. // Copyright © 2016年 itcast. All rights reserved. // #import "HMEmoticonTextView.h" #import "HMEmoticonAttachment.h" #import "HMEmoticonInputView.h" @implementation HMEmoticonTextView { UILabel *_placeHolderLabel; UILabel *_lengthTipLabel; NSMutableArray *_lengthTipLabelCons; /// 表情输入视图 HMEmoticonInputView *_emoticonInputView; } #pragma mark - 设置属性 - (BOOL)isUseEmoticonInputView { return self.inputView != nil; } - (void)setUseEmoticonInputView:(BOOL)useEmoticonInputView { if (self.isUseEmoticonInputView == useEmoticonInputView) { return; } self.inputView = (self.inputView == nil) ? _emoticonInputView : nil; [self reloadInputViews]; } - (void)setPlaceholder:(NSString *)placeholder { _placeholder = placeholder.copy; _placeHolderLabel.text = _placeholder; } - (void)setMaxInputLength:(NSInteger)maxInputLength { _maxInputLength = maxInputLength; [self textChanged]; } - (void)setFont:(UIFont *)font { _placeHolderLabel.font = font; [super setFont:font]; } - (void)setText:(NSString *)text { [super setText:text]; [self textChanged]; } - (UIColor *)textColor { UIColor *color = [super textColor]; return (color == nil) ? [UIColor darkGrayColor] : color; } - (void)setAttributedText:(NSAttributedString *)attributedText { [super setAttributedText:attributedText]; [self textChanged]; } #pragma mark - 构造函数 - (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer { self = [super initWithFrame:frame textContainer:textContainer]; if (self) { self.textColor = [UIColor darkGrayColor]; self.font = [UIFont systemFontOfSize:18]; [self prepareUI]; } return self; } - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { [self prepareUI]; } return self; } - (void)willMoveToWindow:(UIWindow *)newWindow { [super willMoveToWindow:newWindow]; [self layoutIfNeeded]; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - 公共函数 - (NSString *)emoticonText { NSAttributedString *attributeText = self.attributedText; NSMutableString *stringM = [NSMutableString string]; [attributeText enumerateAttributesInRange: NSMakeRange(0, attributeText.length) options:0 usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) { HMEmoticonAttachment *attachment = attrs[@"NSAttachment"]; if (attachment != nil) { [stringM appendString:attachment.text]; } else { [stringM appendString:[attributeText.string substringWithRange:range]]; } }]; return stringM.copy; } - (void)insertEmoticon:(HMEmoticon *)emoticon isRemoved:(BOOL)isRemoved { if (isRemoved) { [self deleteBackward]; return; } if (emoticon.isEmoji) { [self replaceRange:[self selectedTextRange] withText:emoticon.emoji]; return; } NSMutableAttributedString *attributeText = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText]; NSAttributedString *emoticonStr = [HMEmoticonAttachment emoticonStringWithEmoticon:emoticon font:self.font textColor:self.textColor]; NSRange range = self.selectedRange; [attributeText replaceCharactersInRange:range withAttributedString:emoticonStr]; self.attributedText = attributeText; self.selectedRange = NSMakeRange(range.location + 1, 0); [self.delegate textViewDidChange:self]; [[NSNotificationCenter defaultCenter] postNotificationName:UITextViewTextDidChangeNotification object:self]; } - (void)updateTipLabelBottomConstraints:(UIView *)view { // 判断 view 是否是当前的子视图 if (![self.subviews containsObject:view]) { NSLog(@"当前仅支持相对 textView 子视图的控件参照"); return; } [self removeConstraints:_lengthTipLabelCons]; [_lengthTipLabelCons removeAllObjects]; CGFloat margin = 5; [_lengthTipLabelCons addObject:[NSLayoutConstraint constraintWithItem:_lengthTipLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:-margin]]; [_lengthTipLabelCons addObject:[NSLayoutConstraint constraintWithItem:_lengthTipLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeTop multiplier:1.0 constant:-margin]]; [self addConstraints:_lengthTipLabelCons]; } #pragma mark - 监听方法 - (void)textChanged { _placeHolderLabel.hidden = self.hasText; if (_maxInputLength <= 0) { _lengthTipLabel.hidden = YES; return; } _lengthTipLabel.hidden = NO; NSInteger len = _maxInputLength - self.emoticonText.length; _lengthTipLabel.text = [NSString stringWithFormat:@"%zd", len]; _lengthTipLabel.textColor = (len >= 0) ? [UIColor lightGrayColor] : [UIColor redColor]; } #pragma mark - 设置界面 - (void)prepareInputView { __weak typeof(self) weakSelf = self; _emoticonInputView = [[HMEmoticonInputView alloc] initWithSelectedEmoticon:^(HMEmoticon * _Nullable emoticon, BOOL isRemoved) { [weakSelf insertEmoticon:emoticon isRemoved:isRemoved]; }]; } - (void)prepareUI { // 0. 准备输入视图 [self prepareInputView]; // 1. 注册文本变化通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged) name:UITextViewTextDidChangeNotification object:nil]; // 2. 默认属性 self.alwaysBounceVertical = YES; self.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag; // 3. 尺寸视图 UIView *sizeView = [[UIView alloc] init]; sizeView.backgroundColor = [UIColor clearColor]; sizeView.userInteractionEnabled = NO; [self insertSubview:sizeView atIndex:0]; sizeView.translatesAutoresizingMaskIntoConstraints = NO; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[sizeView]-0-|" options:0 metrics:nil views:@{@"sizeView": sizeView}]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[sizeView]-0-|" options:0 metrics:nil views:@{@"sizeView": sizeView}]]; [self addConstraint:[NSLayoutConstraint constraintWithItem:sizeView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]]; [self addConstraint:[NSLayoutConstraint constraintWithItem:sizeView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]]; // 4. 占位标签 _placeHolderLabel = [[UILabel alloc] init]; _placeHolderLabel.text = _placeholder; _placeHolderLabel.textColor = [UIColor lightGrayColor]; _placeHolderLabel.font = self.font; _placeHolderLabel.numberOfLines = 0; [self addSubview:_placeHolderLabel]; _placeHolderLabel.translatesAutoresizingMaskIntoConstraints = NO; CGFloat leftOffset = 5; CGFloat topOffset = 8; [self addConstraint:[NSLayoutConstraint constraintWithItem:_placeHolderLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:leftOffset]]; [self addConstraint:[NSLayoutConstraint constraintWithItem:_placeHolderLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:topOffset]]; [self addConstraint:[NSLayoutConstraint constraintWithItem:_placeHolderLabel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:-2 * leftOffset]]; // 5. 长度提示标签 _lengthTipLabel = [[UILabel alloc] init]; _lengthTipLabel.text = [NSString stringWithFormat:@"%zd", _maxInputLength]; _lengthTipLabel.textColor = [UIColor lightGrayColor]; _lengthTipLabel.font = [UIFont systemFontOfSize:12]; [self addSubview:_lengthTipLabel]; _lengthTipLabel.translatesAutoresizingMaskIntoConstraints = NO; _lengthTipLabelCons = [NSMutableArray array]; [_lengthTipLabelCons addObject:[NSLayoutConstraint constraintWithItem:_lengthTipLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:-leftOffset]]; [_lengthTipLabelCons addObject:[NSLayoutConstraint constraintWithItem:_lengthTipLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-leftOffset]]; [self addConstraints:_lengthTipLabelCons]; } @end