效果图
效果图关键方法
- (CGSize)sizeThatFits:(CGSize)size;
思路
1、使用sizeThatFits方法获取UITextView的实时高度
2、监听键盘的出现和消失事件,改变输入框的y,调整输入框的位置
3、- (void)textViewDidChange:(UITextView *)textView 方法中获取输入内容
4、通过代理模式,将数据传递给指定对象
5、代理方法中使用Block作为参数,带回处理结果,如消息发送成功
6、添加透明的背景视图用于收起键盘
方法实现关键点
1、sizeThatFits
CGSize size = [textView sizeThatFits:CGSizeMake(textView.frame.size.width, MAXFLOAT)];
NSLog(@"textView高度:%f",size.height);
2、监听键盘
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
事件处理
// !!!: 键盘显示
-(void)keyboardWillShow:(NSNotification*)notification{
NSDictionary *dic =notification.userInfo;
// 获取键盘的frame
NSNumber *keyboardFrame= dic[@"UIKeyboardFrameEndUserInfoKey"];
CGRect keyboardFrameNew = keyboardFrame.CGRectValue;
// 动画时间
float animationTime = [dic[@"UIKeyboardAnimationDurationUserInfoKey"]floatValue];
// 动画速度
int animationCurve =[dic[@"UIKeyboardAnimationCurveUserInfoKey"]intValue];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationTime];
[UIView setAnimationCurve:animationCurve];
// 控件位置、样式设置
// do something
[UIView commitAnimations];
}
// !!!: 键盘隐藏
-(void)keyboardWillHide:(NSNotification*)notification{
NSDictionary *dic = notification.userInfo;
// 获取动画时间
float animationTime = [dic[@"UIKeyboardAnimationDurationUserInfoKey"]floatValue];
// 获取动画的速度
int animationCurve =[dic[@"UIKeyboardAnimationCurveUserInfoKey"]intValue];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationTime];
[UIView setAnimationCurve:animationCurve];
// 控件位置、样式设置
// do something
[UIView commitAnimations];
}
3、获取输入内容
-(void)textViewDidChange:(UITextView *)textView{
self.content = textView.text; // 输入的内容
}
4、代理传递数据
其中的block带回消息处理结果
@optional
-(void)commentView:(CommentView*)commentView sendMessage:(NSString*)message complete:(void(^)(BOOL success))completeBlock;
5、收起键盘
收起键盘的视图因为超过父视图,正常是不会响应点击事件的,我们通过修改以下方法的返回值改变响应链
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
// !!!: 超过父视图的子视图可以点击
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
NSArray *subViews = self.subviews;
if ([subViews count] > 1)
{
for (UIView *aSubView in subViews)
{
if ([aSubView pointInside:[self convertPoint:point toView:aSubView] withEvent:event])
{
return YES;
}
}
}
if (point.x > 0 && point.x < self.frame.size.width && point.y > 0 && point.y < self.frame.size.height)
{
return YES;
}
return NO;
}
使用方法
上面简单介绍了实现过程,下面来看下如何使用
1、声明一个输入框视图,并实现代理
@interface ViewController ()<CommentViewDelegate>
@property (strong ,nonatomic) CommentView *commentTool;
@end
2、懒加载方式初始化该视图
-(CommentView *)commentTool{
if (_commentTool==nil) {
NSArray *itemArray = [[NSBundle mainBundle] loadNibNamed:@"CommentView" owner:nil options:nil];
for (id item in itemArray) {
if ([item isMemberOfClass:[CommentView class]]) {
_commentTool = item;
_commentTool.y = kScreenHeight - _commentTool.viewHeight;
_commentTool.delegate = self;
}
}
}
return _commentTool;
}
3、实现其代理方法
// !!!: 评论的代理
-(void)commentView:(CommentView *)commentView sendMessage:(NSString *)message complete:(void (^)(BOOL))completeBlock{
DLog(@"评论:%@",message);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.label.text = message;
// 收起键盘
completeBlock(YES); // 发送成功时
// completeBlock(NO); // 发送失败时
});
}
Demo地址
总结
1、在输入内容时,因为size的变化,需要重新调整控件的frame;在收回控件时如果需要回复到最初状态,需要记录最初的状态,当父视图addsubview时会触发子视图-(void)willMoveToSuperview:(UIView *)newSuperview事件,这里可以得到父视图,并且记录控件的最初状态、位置等信息
2、我们知道,有返回值的代理方法使用的在主线程中,显然这种方式并不适合在:B将事件传递给A,并希望A在某一不确定时刻再将事件传递回来,这样的情况里,所以我使用Block传递这不确定时刻的消息,你也可以使用通知、子控件传递过来等方式实现相同效果,不过个人觉得block会更简洁
3、这个自适应输入框存在Bug:如果页面有滑动返回手势时,键盘的将要消失出现事件下改变控件的width时,会出现子控件的frame错误,不知道时系统本身问题还是什么,解决方案有两个:1、取消滑动返回手势 2、控件本身不改变最初的width,只做整体控件y的更改
4、针对3的问题,如果读者有更好的解决方案,希望留言回复,谢谢
网友评论