iOS 仿即刻评论输入框带图片

效果图
效果图.gif
功能点
  1. 输入框自适应高度
  2. 达到某个高度高度不再变化
  3. 底部显示图片
  4. 键盘的弹起和收起适应
用到的控件
  • UITextView
  • UIImageView
  • UIScrollView
  • UIButton
  • UIView
思路

UIScrollViewcontentSizeUITextView 的文字高度 + 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;

其中对 GPTextViewplaceholder 实现这里就不描述了,主要是描述一下输入框的自适应高,首先注册 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];
}

autoChangeHeightYES 时,才对 commentView 的高度进行改变,否则只更新 textView 的高度和 scrollViewcontentSize 属性。

  • 选择图片的时候,如果是第一次选择图片 commentView 的高度就需要改变,在 imagePickerController:didFinishPickingMediaWithInfo 中进行操作
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)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 输入框的一个实现

代码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 一、简介 <<UITextView(文本视图) : UITextView可以输入多行文字并且可以滚动显示浏览全文的...
    无邪8阅读 8,144评论 6 1
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,609评论 4 59
  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,494评论 1 180
  • 在车上一掠而过之际,忽地憋见一栋崭新的建筑,只见上面挂着“仕馨月子”几个大字。 如果没记错的话,这里原先是一块荒芜...
    笔墨曹西西阅读 225评论 0 2
  • 生活的轨迹有时候会穿插着厄运,同时也会给人希望。 ——题记 《釜山行》,一部讲述单亲父亲...
    时光微澜亦安然阅读 157评论 0 0