支持自动布局,自动高度变化和palceholder的输入框

支持自动布局,自动高度变化和palceholder的输入框

2016-03-03 06:16  编辑: tujinqiu  分类:iOS开发  来源:KevinTing 投稿
2  259
招聘信息:
  • iOS 研发工程师
  • 资深iOS开发工程师
  • iOS开发工程师
  • IOS开发工程师
  • iOS、Android、java开发工程师
  • java高级软件工程师
  • iOS软件工程师
  • Web后端高级开发工程师
  • Mac开发工程师
  • iOS技术专家
  • 高级PHP开发工程师
 

本文是投稿文章,作者:KevinTing

不罗嗦,这是效果,输入框自动弹起弹下。输入框自动伸缩大小,同时上面的tableView也能自动伸缩。

输入框.gif

关于输入框自动弹起弹下我另开一篇描述下,这里就不说了,这里重点关注输入框的自动伸缩和palceholder的实现。

1、继承UITextView

这个是用Auto Layout实现的!当然和网上很多教程一样,这个输入框是用UITextView实现的,不然怎么放大缩小?

1
2
3
4
5
6
7
8
9
10
11
12
#import [UIKit/UIKit.h](因识别问题,此处将尖括号改为方括号)
 
IB_DESIGNABLE
 
@interface KTAutoHeightTextView : UITextView
 
// 是否显示圆角边界
@property (nonatomic, assign) IBInspectable BOOL showsRoundCorner;
// placeholder
@property (nonatomic, copy) IBInspectable NSString *placeholder;
 
@end

这里IB_DESIGNABLE宏和IBInspectable配合使用是为了在xib和storyboard里面可以直接设置属性并实时显示变化效果。showsRoundCorner属性是为了能模仿UITextField的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)setShowsRoundCorner:(BOOL)showsRoundCorner
{
     _showsRoundCorner = showsRoundCorner;
     if  (showsRoundCorner)
     {
         self.layer.masksToBounds = YES;
         self.layer.cornerRadius = 5.0;
         self.layer.borderColor = [[UIColor lightGrayColor] CGColor];
         self.layer.borderWidth = 0.5;
     }
     else
     {
         self.layer.masksToBounds = NO;
         self.layer.cornerRadius = 0.0;
         self.layer.borderColor = [[UIColor clearColor] CGColor];
         self.layer.borderWidth = 0.0;
     }
}

然后在你在storyboard里面拖一个UITextView,然后设置class为KTAutoHeightTextView,接着你会发现可以设置属性,这是IB_DESIGNABLE宏和IBInspectable的功劳,设置showsRoundCorner为ON,然后storyboard中就会显示有圆角了!

支持自动布局,自动高度变化和palceholder的输入框_第1张图片

760FD1EC-8B46-42BA-8F50-DD8712D9F6BE.png

支持自动布局,自动高度变化和palceholder的输入框_第2张图片

31C1E98A-3C82-4801-AAF4-7EF2AA519F7F.png

2、placeholder实现

同样实现placeholder的setter,代码很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (void)setPlaceholder:(NSString *)placeholder
{
     _placeholder = placeholder;
     if  (placeholder)
     {
         if  (!_placeholderLabel)
         {
             _placeholderLabel = [[UILabel alloc] init];
             _placeholderLabel.font = self.font;
             _placeholderLabel.textAlignment = NSTextAlignmentLeft;
             UIEdgeInsets inset = self.textContainerInset;
             CGRect bounds = self.bounds;
             _placeholderLabel.frame = CGRectMake(4.0, inset.top, bounds.size.width - inset.left - inset.right, self.font.lineHeight);
             _placeholderLabel.textColor = [UIColor colorWithWhite:0.801 alpha:1.000];
             [self addSubview:_placeholderLabel];
             _placeholderLabel.text = placeholder;
         }
         _placeholderLabel.hidden = self.text.length > 0;
     }
     else
     {
         if  (_placeholderLabel)
         {
             [_placeholderLabel removeFromSuperview];
             _placeholderLabel = nil;
         }
     }
}

placeholder还需要在编辑的时候显示或者隐藏,那么这里监听text的长度变化就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- (instancetype)initWithFrame:(CGRect)frame
{
     if  (self = [ super  initWithFrame:frame])
     {
         [self setup];
     }
     
     return  self;
}
 
- (void)awakeFromNib
{
     [self setup];
}
 
- (void)dealloc
{
     [[NSNotificationCenter defaultCenter] removeObserver:self];
}
 
- (void)setup
{
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTextDidChange:) name:UITextViewTextDidChangeNotification object:self];
}
 
- (void)handleTextDidChange:(NSNotification *)notif
{
     if  (!self.placeholder)
     {
         return ;
     }
     
     UITextView *textView = notif.object;
     self.placeholderLabel.hidden = textView.text.length > 0;
}

也很简单,值得注意的是awakeFromNib是考虑到从xib或者storyboard中加载的时候并不会调用initWithFrame而加上的。为了placeholder的font能跟随UITextView本身的font变化,这里有个小细节,重写font的setter

1
2
3
4
5
6
7
8
9
10
11
- (void)setFont:(UIFont *)font
{
     [ super  setFont:font];
     
     if  (_placeholderLabel)
     {
         UIEdgeInsets insets = self.textContainerInset;
         CGRect bounds = self.bounds;
         _placeholderLabel.frame = CGRectMake(4.0, insets.top, bounds.size.width - insets.left - insets.right, font.lineHeight);
     }
}

3、自动高度变化的实现

在对textView进行输入的时候,如果换行了,那么你会发现属性contentSize会改变。这里就是利用contentSize的变化进行缩放高度。这里并没有去计算一堆的boundingRectWithSize: options: attributes: context:因为发现这个有时候并不准!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
- (void)layoutSubviews
{
     [ super  layoutSubviews];
     
     self.layoutFinished = YES;
}
 
- (void)setContentSize:(CGSize)contentSize
{
     [ super  setContentSize:contentSize];
     // 监听size变化
     if  (self.font)
     {
         if  (self.layoutFinished)  // 更新约束或者大小
         {
             CGFloat fitHeight = [self sizeThatFits:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)].height;
             if  (fabs(fitHeight - self.bounds.size.height) < self.font.lineHeight * 0.5)  // 变化量小于一个行距的0.5倍
             {
                 [self findHeightConstraint];
                 return ;
             }
             if  (self.heightConstraint)
             {
                 self.heightConstraint.constant = fitHeight;
                 [self layoutIfNeeded];
             }
             else
             {
                 CGRect bounds = self.bounds;
                 bounds.size.height = fitHeight;
                 self.bounds = bounds;
             }
         }
         else  // 查找height约束,记录初值
         {
             [self findHeightConstraint];
         }
     }
}
 
- (void)findHeightConstraint
{
     if  (self.heightConstraint)
     {
         return ;
     }
     for  (NSLayoutConstraint *constraint  in  self.constraints)
     {
         if  (constraint.secondItem == nil && constraint.firstAttribute == NSLayoutAttributeHeight)
         {
             self.heightConstraint = constraint;
             break ;
         }
     }
}

代码是在利用sizeThatFits在contentSize变化的时候去计算合适的大小fitHeight,同时利用一定的规则在合适的时候寻找高度约束heightConstraint。当存在heightConstraint的时候修改约束值,不存在的时候直接修改bounds,从而实现在自动布局或者非自动布局情况下都能自动匹配高度。

4、如何设置约束

如果在自动布局情况下使用KTAutoHeightTextView,应注意:1、显然你应该有Height这个约束;2、在高度方向上你应该用KTAutoHeightTextView的height去驱动其他view的height而不是反过来。第一句easy不解释,第二句见下图,KTAutoHeightTextView设置了5个约束(一个height,还有上下左右),它的superView(明显起见,我把它设成了灰色)并没有设置height约束。那么它的height就会根据KTAutoHeightTextView的height和上,下三个约束来确定。从而达到这样的效果:KTAutoHeightTextView伸缩,superView也伸缩,上面的tableView也伸缩。恩,这是我们想要的效果!

完整的项目https://github.com/tujinqiu/KTAutoHeightTextView

你可能感兴趣的:(动态计算高度,文本高度自适应)