#import <UIKit/UIKit.h>
@interface JQTextView : UITextView
@property (nonatomic,copy)IBInspectable NSString *placeHoder;//提示语
@property (nonatomic,weak)UIView *popView;//键盘frame改变时,操作的view
@property (nonatomic,strong)NSLayoutConstraint *constrainH;//高度约束
@end
#define JQMainScreenSize [UIScreen mainScreen].bounds.size
#define JQPlacehoderPadding 8 //提示语与边框的距离(上下左)
#import "JQTextView.h"
@interface JQTextView () {
UILabel *_placehoderLbl;//提示框
}
@end
@implementation JQTextView
#pragma mark 生命周期方法
- (void)awakeFromNib{
//1.自我初始化
//->设置默认字体\提示字体
self.translatesAutoresizingMaskIntoConstraints = NO;//允许autoLayout
self.backgroundColor=[UIColor whiteColor];//默认背景色
self.contentMode = UIViewContentModeCenter;
self.layer.cornerRadius = 6;//圆角
//2.添加子控件
[self addSubView];
//3.清空text:(可能在故事板中拖动的时候没有清空文本)
self.text = @"";
//3.监听textView文字通知
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(textDidChange) name:UITextViewTextDidChangeNotification object:self];
//4.监听键盘的弹起和收缩
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyBoardChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
- (void)dealloc{
//移除所有通知
[[NSNotificationCenter defaultCenter]removeObserver:self];
}
#pragma mark 控件相关
- (void)layoutSubviews{
[super layoutSubviews];
[self setupFrame];
[self autoFitHeight];
}
- (void)addSubView{
//1.添加子控件
_placehoderLbl=[[UILabel alloc]init];
//->设置默认提示文字
_placehoderLbl.text=(self.placeHoder.length>0?self.placeHoder:@"JQTextView默认提示");
//->默认字体 == textView字体
_placehoderLbl.font=self.font;
//->设置默认字体颜色
_placehoderLbl.textColor=[UIColor lightGrayColor];
//->设置默认字体透明度
_placehoderLbl.alpha=0.8;
//->提示框也可以多行
_placehoderLbl.numberOfLines=0;
[self addSubview:_placehoderLbl];
}
/**
* 设置提示标签的frame
*/
- (void)setupFrame{
CGRect frame = self.bounds;
frame.origin.x = JQPlacehoderPadding;
frame.size.width -= 2*JQPlacehoderPadding;
_placehoderLbl.frame = frame;
/*
为了提示语和textView周围有有一定的距离
提示标签的x和weight做了调整
*/
}
#pragma mark 其他方法
/**
* 通过字体和最大尺寸计算文字所占size
*/
-(CGSize)sizeWithString:(NSString *)str Font:(UIFont *)font maxSize:(CGSize)maxSize
{
NSDictionary *attrs = @{NSFontAttributeName : font};
return [str boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
}
/**
* 自适应高度
*/
- (void)autoFitHeight{
CGFloat curW = self.frame.size.width;
CGFloat H = 0;
if (_placehoderLbl.hidden) { //如果提示语隐藏了,那么使用textView的高度赋值高度约束
self.constrainH.constant = self.contentSize.height;
}
else{
//约束高度 == 提示语文字高度+2*提示语跟textView之间的距离
H = [self sizeWithString:_placehoderLbl.text Font:_placehoderLbl.font maxSize:CGSizeMake(curW, MAXFLOAT)].height;
self.constrainH.constant = H+2*JQPlacehoderPadding;
}
/*
为什么不直接都用self.contentSize.height呢?
因为如果提示语超过了一行的话,那么self.contentSize.height得到的也只是一行内容
应占的高度
*/
[self layoutIfNeeded];
}
#pragma mark 点击/响应通知方法
/**
* 每一次文本改变时调用
*/
- (void)textDidChange{
//提示标签隐藏与否
_placehoderLbl.hidden=(self.text.length>0);
//自适应高度
[self autoFitHeight];
//滚动到最后一行文字
[self scrollRangeToVisible:NSMakeRange(self.text.length, 1)];
}
- (void)keyBoardChange:(NSNotification *)note{
// 0.取出键盘动画的时间
CGFloat duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// 1.取得键盘最后的frame
CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// 2.计算控制器的view需要平移的距离
CGFloat transformY = keyboardFrame.origin.y - self.popView.frame.size.height;
// 3.执行动画
[UIView animateWithDuration:duration animations:^{
self.popView.transform = CGAffineTransformMakeTranslation(0, transformY);
}];
}
#pragma mark 公开方法
- (void)setPlaceHoder:(NSString *)placeHoder{
_placeHoder = placeHoder;
_placehoderLbl.text = [placeHoder copy];
/*为什么设置了提示语后,不调用autoFitHeight方法来更新高度呢?
因为autoLayoutSubviews方法里面调用了,而且autoLayoutSubviews
是在当前方法的后面被调用*/
}
#pragma mark 重写方法
- (void)setFont:(UIFont *)font{
[super setFont:font];
_placehoderLbl.font=font;
}
/**
* 直接给textField赋值时使用
*/
- (void)setText:(NSString *)text{
[super setText:text];
[self textDidChange];
}
@end
使用方法:
1.在IB中添加textView
2.修改textView对应的类为JQTextView
3.设置提示语
4.设置好约束(高度随意,因为会自适应)
5.在控制器中引用textView
6.在控制器中引用高度约束
7.在代码中给textView.constrainH赋值高度约束
8.在代码中给textView.popView赋值