学习计划 (1)- 自定义文本框

2017-12-19  本文已影响23人  如风如花不如你
我这做的大概内容是:
自定义控件
UITableView在项目中遇到的实际问题探讨
iOS系统自带的动画
自定义动画
WKWebView中在实际项目中遇到的问题探讨
还有就是一些可能看到哪个软件上面的效果想自己实现以下的吧。
当然,如果大家有什么想要实现的,也可以留言,我可以考虑实现一下的,也免得我去想100个项目了,毕竟比较懒。

第一天嘛,就简单点,来个自定义的文本框,也是我项目中实际出现的:


手机号输入框.png 密码输入框.png 提现输入框.png

如大家所看到那样,就是三种输入框,这个在很多软件上都有,实现起来也比较简单。

第一种:透明带图的框
就是边框色没有了,宽度也没有了。leftView设置成了图片,文字往左边移动了一点点
第二种: 同理的边框透明,但leftView设置了文字
第三种区别就是多了rightView

仔细分析可以看出来,它们其实都是一种文本输入框,只是一些样式的变化。所以可以做成一种类起对待。
只不过我们在创建的时候区分一下就行了。
区分某种类别,一般是用枚举了,因为差异就是是否是具有下划线的。所以我们大致分为两种:

typedef NS_ENUM(NSInteger,GWTextFieldStyle){
    GWTextFieldStyleWithNormal,    //正常
    GWTextFieldStyleWithUnline,    //下滑线
};

然后由于属性的多样化,我们的需求可能根据产品汪的变化而变化,但是我们又不想随便的去更改一个已经写好的类,所以我是决定使用一个model来作为参数,这样的话,以后修改model就可以了,不会去干扰样式代码造成什么问题。

#import <Foundation/Foundation.h>
/**
 自定义输入框控件数据源
 */
@interface GWTextFieldModel : NSObject
//标题内容
@property (nonatomic,readwrite,strong)id titleViewContent;
//右视图内容
@property (nonatomic,readwrite,strong)id rightViewContent;
//文本颜色
@property (nonatomic,readwrite,strong)UIColor *textColor;
//边框线宽度
@property (nonatomic,readwrite,unsafe_unretained)CGFloat borderWidth;
//边框线颜色
@property (nonatomic,readwrite,strong)UIColor *borderColor;
//背景颜色
@property (nonatomic,readwrite,strong)UIColor *backgroundColor;
//默认提示语
@property (nonatomic,readwrite,copy)NSString *placeholder;
//默认提示语颜色
@property (nonatomic,readwrite,strong)UIColor *placeHolderColor;
//键盘类型
@property (nonatomic,readwrite,unsafe_unretained)UIKeyboardType keyboardType;
//是否显示密码
@property (nonatomic,readwrite,unsafe_unretained)BOOL isPassword;

@end

可以看到我在model当中存在了两个特殊的参数,貌似与属性无关,而且还是id类型

titleViewContent
rightViewContent

我是设想通过titleViewContent就可以直接分辨出文字或者视图,使得在调用的时候直接填入参数就可以了。
而rightViewContent也是同理,如果添加了rightView的情况下,我们加入即可。也就是说,我们只需要填入参数,其它我们在内部直接进行实现。

创建好model之后,我们就可以创建我们的自定义文本框了:

#import <UIKit/UIKit.h>
@class GWTextFieldModel;

typedef NS_ENUM(NSInteger,GWTextFieldStyle){
    GWTextFieldStyleWithNormal,    //正常
    GWTextFieldStyleWithUnline,    //下滑线
};


/**
 自定义输入框控件类
 */
@interface GWTransTextField : UITextField<UITextFieldDelegate>


/**
 创建一个默认样式的输入框·

 @param frame 布局
 @param textFieldModel 文本框属性设置数据源
 @return instance of GWTransTextField
 */
+ (instancetype)createNormalTextFieldWithFrame:(CGRect)frame
                                textFieldModel:(GWTextFieldModel *)textFieldModel;


/**
 创建一个带有下划线的输入框

 @param frame 布局
 @param textFieldModel 文本框属性设置数据源
 @return instance of GWTransTextField
 */
+(instancetype)createUnlineTextFieldWithFrame:(CGRect) frame
                               textFieldModel:(GWTextFieldModel *)textFieldModel;


这里有几个习惯需要注意的:

1. 在.h文件中,一般不要直接#import一个类,使用@class就可以了,这样就不用一编译时就导入文件了。
然后在.m文件中使用#import导入文件,也就是说我们只需要在运行时去导入文件。

2. 给类添加注释。有必要的话,最好还要解释一下这个类的功能。 这样方便你自己和别人第一时间知道这个类的作用。
短时间还好,时间长了,对于类没那么熟悉的时候,就会产生迟钝,造成多余的熟悉时间成本。

3.就是写注释了,这是个必须的工作。

从上面方法的创建,我们可以看到,我将其分为默认类型的,就是我们常用的文字开头,不透明文本框,还有一种带下划线且透明的输入框。

那么我们在是实现的时候,就可以了解到,我们在实现默认文本框的时候,自由度比较高,它们的属性基本都是由外界的model所决定。而具有下划线的文本框,很多样式已经固定了。所以就形成了如下的结果:

#pragma mark - 初始化默认文本框
- (instancetype)initWithNormalFrame:(CGRect)frame textFieldModel:(GWTextFieldModel *)textFieldModel{
    if(self = [super initWithFrame:frame])
    {
        textFieldModel.placeHolderColor = nil;
        [self createUILayoutWithModel:textFieldModel textFieldStyle:GWTextFieldStyleWithNormal];
    }
    return  self;
}
#pragma mark - 初始化下划线文本框
- (instancetype)initWithUnlineFrame:(CGRect)frame textFieldModel:(GWTextFieldModel *)textFieldModel{
    if(self = [super initWithFrame:frame])
    {
        textFieldModel.placeHolderColor = [PMBTools colorWithHexString:@"#BEBEBE"];
        textFieldModel.borderWidth = 0;
        textFieldModel.textColor = [UIColor whiteColor];
        textFieldModel.borderColor = [UIColor clearColor];
        textFieldModel.backgroundColor = [UIColor clearColor];
        [self createUILayoutWithModel:textFieldModel textFieldStyle:GWTextFieldStyleWithUnline];
    }
    return  self;
}

可以看到,我们在设定默认文本框方法的时候,里面并没有具体的参数设置,而在下划线的初始化方法中,我们设置了很多默认的属性。也就是说,将必要的东西尽量去封装。

这里要提的习惯就是:

使用#pragma mark 命令,通过该命令,可以形成如下图的导航效果,可以快速定位你写的方法。
使用#pragma mark - 命令可以形成分组效果
导航.png

下面我们就进入到重点了,如何进行识别titleViewContent的类型?

if([textFieldModel.titleViewContent isKindOfClass:[NSString class]]){
      //实现将文本框作为leftView
}else if([textFieldModel.titleViewContent isKindOfClass:[UIImage class]]){
      //实现将图片设为leftView
}else if([textFieldModel.titleViewContent isKindOfClass:[UIView class]]){
      //实现将普通视图设为leftView
}

if([textFieldModel.rightViewContent isKindOfClass:[UIView class]]){
      //实现将普通视图设为rightView
}

通过isKindOfClass方法,我们判断出我们的titleViewContent是什么类型的,我们就可以根据不同的类型进行处理。
当然咯,你会发现这样做之后,单纯的去设置leftView什么的还是显示不出来,那是因为,leftViewMode的问题

typedef NS_ENUM(NSInteger, UITextFieldViewMode) {
    UITextFieldViewModeNever,
    UITextFieldViewModeWhileEditing,
    UITextFieldViewModeUnlessEditing,
    UITextFieldViewModeAlways
};

系统默认的是UITextFieldViewModeNever,它并不会显示,所以我们要将该值设置成UITextFieldViewModeAlways,让它一直显示。

self.leftView = _titleView;
self.leftViewMode = UITextFieldViewModeAlways;

当我们做完基本的属性设置之后,就可以调用,并显示了。

@property (nonatomic,readwrite,strong)GWTransTextField *txtUserName;

- (GWTransTextField *)txtUserName{
    if(!_txtUserName){
        CGFloat margin = 20;
        CGFloat height = 50;
        GWTextFieldModel *textFieldModel = [GWTextFieldModel new];
        textFieldModel.placeholder = @"请输入手机密码";
        textFieldModel.titleViewContent = [UIImage imageNamed:@"Phone"];
        _txtUserName = [GWTransTextField createUnlineTextFieldWithFrame:
                       (CGRect){margin,200,kSCREENWIDTH-margin*2,height} textFieldModel:textFieldModel];
    }return _txtUserName;
}

简单的创建,添加之后,我们会发现,效果不太好


开始的效果.png

这图片和字怎么都挤一块去了,完全不符合我们比产品汪还要好的审美啊,所以我们需要调整样式
调整样式需要重写父类方法

- (CGRect)textRectForBounds:(CGRect)bounds

这里的bounds参数,就是当前文本框的bounds,所以直接调整就可以了。

    bounds.origin.x = marginLeft(_titleView) + 15;
    //如果有右视图的话,文本框输入的范围要缩小到右视图的前面
    if(_rightView){
        bounds.size.width = bounds.size.width-bounds.origin.x-_rightView.bounds.size.width-15;
    }

如果还需要调整图标大小,方位什么的需要重写该方法:

- (CGRect)leftViewRectForBounds:(CGRect)bounds{
    bounds.origin.x = 10;
    bounds.origin.y = bounds.size.height/2-imageSize.height/2;
    bounds.size.width = imageSize.width;
    bounds.size.height = imageSize.height;
}
- (CGRect)rightViewRectForBounds:(CGRect)bounds;

如果你在实现的过程中遇到 输入的时候文本框出现样式问题就实现重写如下方法,保持与textRectForBounds方法样式一致了,反正我是遇到过的。

- (CGRect)editingRectForBounds:(CGRect)bounds

最终的样式


手机号输入框.png

如果你需要添加在尾部的清除按钮:


清楚按钮.png
self.clearButtonMode = UITextFieldViewModeWhileEditing;
同理,mode也是分了各种情况,包括,, 
typedef NS_ENUM(NSInteger, UITextFieldViewMode) {
    UITextFieldViewModeNever, //不显示
    UITextFieldViewModeWhileEditing,//有内容,输入的时候显示
    UITextFieldViewModeUnlessEditing,//有内容,不输入的时候显示
    UITextFieldViewModeAlways//有内容,一直显示
};

调用的话如下所示:

- (GWTransTextField *)txtUserName{
    if(!_txtUserName){
        NSInteger buttonMargin = 20;
        CGFloat buttonHeight = 50;
        GWTextFieldModel *textFieldModel = [GWTextFieldModel new];
        textFieldModel.placeholder = @"请输入手机密码";
        textFieldModel.titleViewContent = [UIImage imageNamed:@"Phone"];
        _txtUserName = [GWTransTextField createUnlineTextFieldWithFrame:(CGRect){buttonMargin,200,kSCREENWIDTH-buttonMargin*2,buttonHeight} textFieldModel:textFieldModel];
    }return _txtUserName;
}

然后直接添加到view中即可。

最后三种实现的效果如下:


最终效果.png

最后贴上代码地址:
https://github.com/yanggenwei/GWTextField

上一篇下一篇

猜你喜欢

热点阅读