效果图
功能点
- 输入框自适应高度
- 达到某个高度高度不再变化
- 底部显示图片
- 键盘的弹起和收起适应
用到的控件
UITextView
UIImageView
UIScrollView
UIButton
UIView
思路
UIScrollView
的contentSize
为UITextView
的文字高度 +UIImageView
的高度
UIScrollView
的最大高度为UITextView
显示最多行数的文字高度,加上图片的高度
具体步骤
创建
CommentView
进行封装好,CommentView
需要实现的功能为 UI 界面的搭建、键盘弹起和收起的适应,选择图片之后对界面约束的一些更新操作commentView.h
需要的一些属性
@property (nonatomic, weak) UIViewController *contoller; /**< 将控制器传入,跳转相册 */
@property (nonatomic, weak) MASConstraint *bottomConstraint; /**< CommentView 的底部约束,键盘弹起和收起时更新约束 */
-
commentView.m
属性
@property (nonatomic, strong) UIScrollView *contentScrollView; /**< 评论内容 */
@property (nonatomic, strong) UIView *contentView; /**< 内容视图 */
@property (nonatomic, strong) UIImageView *imageView; /**< 图片 */
@property (nonatomic, strong) GPTextView *textView; /**< 输入框 */
@property (nonatomic, strong) UIButton *chooseImageButton; /**< 选择图片按钮 */
@property (nonatomic, assign) CGFloat imageHeight; /**< 图片高度 */
@property (nonatomic, assign) CGFloat commentHeight; /**< 评论框的高度 */
-
GPTextView
是自定义的一个类,继承于UITextView
,GPTextView.h
文件
typedef void (^ChangeTextHeight)(NSString *text, CGFloat height, BOOL autoChangeHeight); /**< 改变输入框高度回调 */
@interface GPTextView : UITextView
#pragma mark - Property
@property (nonatomic, copy) NSString *placeholder; /**< 占位文字 */
@property (nonatomic, strong) UIColor *placeholderColor; /**< 占位文字颜色 */
@property (nonatomic, strong) UIFont *placeholderFont; /**< 占位文字字体大小 */
@property (nonatomic, assign) NSInteger numberOfLines; /**< 行数 0-无限行 */
#pragma mark - Method
/**
改变输入框的高度
*/
- (void)textHeightDidChange:(ChangeTextHeight)changeText;
@end
-
GPTextView.m
属性
@property (nonatomic, assign) CGFloat textHeight; /**< 文本高度 */
@property (nonatomic, assign) CGFloat maxTextHeight; /**< 文本最大高度 */
@property (nonatomic, strong) UILabel *placeholderLabel; /**< 占位文字 */
@property (nonatomic, assign) BOOL autoChangeHeight; /**< 是否需要自动改变高度 */
@property (nonatomic, copy) ChangeTextHeight changeTextHeightBlock;
其中对 GPTextView
的 placeholder
实现这里就不描述了,主要是描述一下输入框的自适应高,首先注册 UITextViewTextDidChangeNotification
通知,监听 textView
输入文字的改变;然后在监听方法里面进行判断是否需要改变 textView
的高度
- (void)textDidChange {
self.placeholderLabel.hidden = self.text.length > 0 ? YES : NO; // 占位文字的隐藏和显示
NSInteger height = ceilf([self sizeThatFits:CGSizeMake(self.bounds.size.width, MAXFLOAT)].height); // 计算出当前文字高度
if (self.textHeight != height) { // 高度不一样,行数改变了
// 是否需要改变 CommentView 的高度
self.autoChangeHeight = height <= self.maxTextHeight && self.maxTextHeight > 0;
self.textHeight = height;
if (self.changeTextHeightBlock) {
self.changeTextHeightBlock(self.text, self.textHeight, self.autoChangeHeight);
}
}
}
在 GPTextView
中主要操作就是这些,下面回到 commentView.m
中
- 对键盘的弹起和收起进行监听
- (void)addObserver {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardChange:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardChange:) name:UIKeyboardWillHideNotification object:nil];
}
监听方法实现
- (void)keyboardChange:(NSNotification *)notifi {
NSDictionary *userInfo = notifi.userInfo;
CGFloat duration = [[userInfo valueForKeyPath:@"UIKeyboardAnimationDurationUserInfoKey"] floatValue];
CGFloat keyboardHeight = [[userInfo valueForKeyPath:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue].size.height; /**< 键盘的高度 */
if ([notifi.name isEqualToString:UIKeyboardWillShowNotification]) {
// 键盘弹起
[self updateBottomConstraintsWithHeight:keyboardHeight duration:duration];
} else if ([notifi.name isEqualToString:UIKeyboardWillHideNotification]) {
// 键盘收起
[self updateBottomConstraintsWithHeight:0 duration:duration];
}
}
/**
更新底部约束
*/
- (void)updateBottomConstraintsWithHeight:(CGFloat)height duration:(CGFloat)duration {
self.bottomConstraint.offset(-height);
[UIView animateWithDuration:duration animations:^{
[self.superview layoutIfNeeded];
}];
}
其中 bottomConstraint
是在添加 commentView
时,设置的底部约束的时候赋值好,这个属性在 commentView.h
// 输入框
[self.commentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.view);
self.commentView.bottomConstraint = make.bottom.equalTo(self.view).offset(0);
make.height.mas_equalTo(30);
}];
这样,键盘弹起和收起适配,就不用在外面去控制了
- 输入文字,高度改变,实现
textHeightDidChange
这个方法
[_textView textHeightDidChange:^(NSString *text, CGFloat height, BOOL autoChangeHeight) {
[wkSelf updateHeightWithHeight:height autoChangeHeight:autoChangeHeight];
}];
updateHeightWithHeight:autoChangeHeight
方法实现
- (void)updateHeightWithHeight:(CGFloat)height autoChangeHeight:(BOOL)autoChangeHeight {
CGFloat imageHeight = self.imageView.image ? self.imageHeight : 0;
if (autoChangeHeight) {
// 设置输入框的高度
[self mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(height + imageHeight);
}];
}
// 设置文字可滚动范围
[self.contentView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(height + imageHeight);
}];
[self.textView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(height);
}];
[self layoutIfNeeded];
}
当 autoChangeHeight
为 YES
时,才对 commentView
的高度进行改变,否则只更新 textView
的高度和 scrollView
的 contentSize
属性。
- 选择图片的时候,如果是第一次选择图片
commentView
的高度就需要改变,在imagePickerController:didFinishPickingMediaWithInfo
中进行操作
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info valueForKeyPath:@"UIImagePickerControllerOriginalImage"];
BOOL autoChangeHeight = self.imageView.image ? NO : YES; // 是否需要改变高度 第一次选择图片 YES 切换图片 NO
self.imageView.image = image;
[self layoutIfNeeded];
if (autoChangeHeight) {
[self.imageView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(self.imageHeight);
}];
BOOL isMoreThree = CGRectGetHeight(self.textView.frame) > CGRectGetHeight(self.contentScrollView.frame);
if (isMoreThree) {
// 设置输入框的高度
[self mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(CGRectGetHeight(self.contentScrollView.frame) + self.imageHeight);
}];
// 设置文字可滚动范围
[self.contentView mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(CGRectGetHeight(self.textView.frame) + self.imageHeight);
}];
[self.contentScrollView setContentOffset:CGPointMake(0, self.contentScrollView.contentSize.height - CGRectGetHeight(self.contentScrollView.frame)) animated:YES];
} else {
[self updateHeightWithHeight:self.textView.frame.size.height autoChangeHeight:autoChangeHeight];
}
}
[self layoutIfNeeded];
[picker dismissViewControllerAnimated:YES completion:nil];
}
以上就是仿照即刻 App 输入框的一个实现
代码