ios开发之自定义textView
自定义textView,从理论上讲很简单,根据需求自定义,比如我在开发中的需求就是现实一个字数的限制以及根据输入的文字改变提示剩余字数,那么开始我的基本思路就是自定义一个View,而里面包含一个子控件textView以及占位label和剩余字数现实label,看似很简单但是实际上会遇到很多坑!那么我就首先呢从创建这个自定义textView开始,然后在深入讲讲我在这之中遇到的问题和坑。。
在头文件中我是这样申明的:
/**
* 占位符
*/
@property (nonatomic, copy)NSString *placeholder;
@property (nonatomic, strong)UILabel *placehoderLabel;
/**
* 限制的文字
*/
@property (nonatomic, copy)NSString *limitStr;
/**
* 剩余字数
*/
@property (nonatomic, assign)NSInteger count;
@property (nonatomic, strong)UILabel *countLabel;
@property (nonatomic, weak)id<UITextViewDelegate> textViewDelegate;
@property (nonatomic, strong)UITextView *textView;
在头文件中我公开了很多属性,比如placehoderLabel, textView,countLabel等等,在这我想说明一件事情,这件事情也是我在公司和一位同事意见不合的问题,我认为一切有变化的因子,都应该留出接口让别人能够改变,意思也就是说,在我们自定义一个控件的时候,为了可以让更多的人使用以及复用性,我们一定要留出变化的接口,这样在别人复用的时候,就不用再去内部改变,而只是根据接口去改变这些需要改变的内容即可。
下面贴上我的整个实现代码:
@implementation YHTextView
- (NSString *)placeholder {
if (_placeholder == nil) {
_placeholder = @"请输入文字";
}
return _placeholder;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.layer.borderWidth = 1.f;
_count = 50;
self.countLabel.text = @"剩余50字";
self.textView.height = self.height - 25;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewCharacterChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
return self;
}
- (UITextView *)textView {
if (_textView == nil) {
_textView = [[UITextView alloc] init];
_textView.frame = CGRectMake(0, 0, self.width, self.height);
_textView.delegate = self.textViewDelegate;
_textView.font = TK_FONTSYS(14);
[self addSubview:_textView];
}
return _textView;
}
- (UILabel *)placehoderLabel {
if (_placehoderLabel == nil) {
_placehoderLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 8, 20, 20)];
_placehoderLabel.text = self.placeholder;
_placehoderLabel.textAlignment = NSTextAlignmentLeft;
_placehoderLabel.font = [UIFont systemFontOfSize:14];
_placehoderLabel.textColor = [UIColor lightGrayColor];
[self.textView addSubview:_placehoderLabel];
}
[self sendSubviewToBack:_placehoderLabel];
return _placehoderLabel;
}
- (UILabel *)countLabel {
if (_countLabel == nil) {
_countLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.width-5-100, self.height-5-20, 100, 20)];
_countLabel.textAlignment = NSTextAlignmentRight;
_countLabel.font = [UIFont systemFontOfSize:13];
_countLabel.textColor = [UIColor lightGrayColor];
[self addSubview:_countLabel];
}
return _countLabel;
}
- (void)layoutSubviews {
self.placehoderLabel.frame = CGRectMake(8, 8, self.width, 20);
if (self.textView.text.length>0) {
self.placehoderLabel.hidden = YES;
self.countLabel.text = [NSString stringWithFormat:@"剩余%ld字", self.count - self.textView.text.length];
self.placehoderLabel.hidden = YES;
}
}
- (void)textViewCharacterChanged:(NSNotification *)not {
NSInteger length = self.textView.text.length;
self.placehoderLabel.hidden = YES;
// 获取键盘类型
UITextInputMode *mode = (UITextInputMode *)[UITextInputMode activeInputModes][0];
// 获取当前的键盘语言
NSString *lang = mode.primaryLanguage;
if ([lang isEqualToString:@"zh-Hans"]) {
UITextRange *selectedRange = [self.textView markedTextRange];
UITextPosition *position = [self.textView positionFromPosition:selectedRange.start offset:0];
if (!position) {
if ([self beyondLimitCount:length]) return ;
if (length == 0) {
self.countLabel.text = @"剩余50字";
self.placehoderLabel.hidden = NO;
}
else {
self.countLabel.text = [NSString stringWithFormat:@"剩余%ld字", self.count - length];
self.placehoderLabel.hidden = YES;
}
}
}
else {
if ([self beyondLimitCount:length]) return ;
if (length == 0) {
self.countLabel.text = @"剩余50字";
self.placehoderLabel.hidden = NO;
}
else {
self.countLabel.text = [NSString stringWithFormat:@"剩余%ld字", self.count - length];
self.placehoderLabel.hidden = YES;
}
}
}
- (BOOL)beyondLimitCount:(NSInteger)length {
if (length <= self.count) {
self.textView.editable = YES;
return NO;
}
else {
self.textView.text = [self.textView.text substringToIndex:self.count];
self.countLabel.text = @"剩余0字";
return YES;
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
1>第一点需要注意的是,添加通知和移除通知是必须配对的,我们一般在dealloc方法里移除通知
2>在我的代码中很多控件都是懒加载的方式初始化的,这也是苹果比较推荐的一种方式,懒加载即是控件需要显示的时候加载
3>核心代码都在通知方法里,首先我们需要获取键盘类型,从而获得当前的语言,注意是汉字的话要进行特殊处理,因为汉字在输入的时候有一个高亮状态如果,不作处理,如果说剩余2个字,你想在输入“行”拼音是“xing”,就会只输入xi,无法得到汉字,这也是我遇到比较坑的一个地方。
至于其他部分都是一些比较简单的逻辑处理,在这里就不再做说明了。
ios开发之自定义textView的更多相关文章
- iOS开发之自定义表情键盘(组件封装与自动布局)
下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有MVC, iOS开发中的自动布局,自定义组件的封装与使用,Block回调,CoreData的使用.有的小伙伴可能会问写一个自 ...
- 详解iOS开发之自定义View
iOS开发之自定义View是本文要将介绍的内容,iOS SDK中的View是UIView,我们可以很方便的自定义一个View.创建一个 Window-based Application程序,在其中添加 ...
- 【Swift】IOS开发中自定义转场动画
在IOS开发中,我们model另外一个控制器的时候,一般都使用默认的转场动画. 其实我们可以自定义一些转场动画.达到不同的转场效果. 步骤如下:(photoBrowser是目标控制器) 1.在源控制器 ...
- ios开发中关闭textview控件的虚拟键盘
在ios开发中,textfield控件在点击的时候出现虚拟键盘,关掉虚拟键盘可以通过虚拟键盘中的done button和点击view中的任意地方来关闭虚拟键盘. 1.第一种方法是textfield控件 ...
- iOS开发-UITableView自定义Cell
UITableView在iOS中开发的重要地位是毋庸置疑的,基本上应用中用到的比例是一半左右,而且大部分情况都是需要自定义单元格的,这样用户看到的App才能更有美感.之前写过UITableView的基 ...
- IOS开发之自定义系统弹出键盘上方的view(转载)
这篇文章解决的一个开发中的实际问题就是:当弹出键盘时,自定义键盘上方的view.目前就我的经验来看,有两种解决方法.一个就是利用UITextField或者UITextView的inputAccesso ...
- iOS开发之自定义输入框(利用UITextField及UITextView)
drawRect的工作原理:首先苹果是不推荐我们直接使用drawRect进行工作的,直接调用他也是没有任何效果的.苹果要求我们调用UIView类中的setNeedsDisplay方法,则程序会自动调用 ...
- iOS开发之自定义导航栏返回按钮右滑返回手势失效的解决
我相信针对每一个iOS开发者来说~除了根视图控制器外~所有的界面通过导航栏push过去的界面都是可以通过右滑来返回上一个界面~其实~在很多应用和APP中~用户已经习惯了这个功能~然而~作为开发者的我们 ...
- IOS开发之自定义UITabBarController
UITabBarController是开发中经常会用到的一个视图控制器,但是默认的UITabBarController经常不能够完全满足我们的需求,所以我们经常需要自定义一个UITabBarContr ...
随机推荐
- android之间传递list
Intent intent = new Intent(getActivity(), Activity_Character.class); intent.putExtra("mlTrait&q ...
- 【转】shell学习笔记(三)——引用变量、内部变量、条件测试、字符串比较、整数比较等
1.env显示当前的环境变量 2.PS1='[\u@\h \w \A] \$' 可以设置bash的命令与提示符. 3.echo $$ 显示当前bash的PID号 4.echo $?显示上一条指令的回传 ...
- jenkins之角色授权[六]
标签(linux): jenkins 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 有时候我们公司里面可能有好几个开发团队,当你做完jenkins后,对于测试环 ...
- ferror,perror,cleaner
1.ferror 函数原型: int ferror(FILE *fp) 功能:测试文件是否出现错误 返回值:未出错是0:出错为非0. 每次调用文件输入输出函数,均产生一个新的ferror函数值 fop ...
- form表单中get和post两种提交方式的区别
一.form表单中get和post两种提交方式的区别? 1.get提交表单中的内容在链接处是可见的.post不可见 2.post相比于get是安全的 3.post不收限制大小,get有限制大小(黑马视 ...
- transfer-webpack-plugin最简使用示例
转移文件的插件 加载插件 $ npm install transfer-webpack-plugin --save-dev API new CopyWebpackPlugin(patterns: Ar ...
- Acrobat 转换pdf到png的另一种方法
此方法效率较低,大概2秒转3页pdf成png图片,但是可以保证图片质量很高,分辨率很高.有优化的地方,但没时间研究.先放代码吧. 前提是安装 acrobat 11(即acrobat xi) CAcro ...
- wxPython实现在浏览器中打开链接
需要用到webbrowser模块 代码超简单: import webbrowserwebbrowser.open('http://www.wangxing.com') webbrowser.open( ...
- require和require_once的区别
require 的使用方法如 require("./inc.php"); .通常放在 PHP 程式的最前面,PHP 程式在执行前,就会先读入 require 所指定引入的档案,使它 ...
- Linux C 一个简单的线程池程序设计
最近在学习linux下的编程,刚开始接触感觉有点复杂,今天把线程里比较重要的线程池程序重新理解梳理一下. 实现功能:创建一个线程池,该线程池包含若干个线程,以及一个任务队列,当有新的任务出现时,如果任 ...