背景
UITextField用的也够多了,这两天改一个“修改密码”的bug,结果发现一旦设置了secureTextEntry之后,会有很多的坑,
这里简单总结下:
第一天更新:
// 说明:以下所有的问题点都是New Password这个UITextField
1. keyboard type会改变
背景:
设置当前New Password的keyboardtype是UIKeyboardTypeASCIICapable,
现象:
设置textfield为暗文的时候,正常;但是设置为明文的时候,键盘会变化,看如下截图(注意观察左下角):
这个原因也没查清楚,应该secureTextEntry切换的时候的bug.
那怎么解决呢?
简单来说,既然是明文状态下会出错,那么就在点击其他textfield的时候,将该password textfield设置为暗文即可.
即:可以监听textfield的textfield end edit 状态,并检查不是FirstResponder的时候,设置secureTextEntry为YES
解决代码如下:
- (void)textFieldChanged {
if (![self.textField isFirstResponder]) {
self.textField.secureTextEntry = YES;
}
}
2. 光标不稳定
现象:
当secureTextEntry在YES和NO之间切换的时候,理论上光标应该在最后一个字符后面,但是实际会出现当有暗文变为明文的时候,光标还停留在原来的位置的情况.如下截图,蓝色光标和内容之间的空格:
那怎么解决呢?
这个的解决方法,想的比较容易,让光标重新刷新一下就好.
光标什么时候回刷新呢?
当输入的text改变的时候会刷新,于是考虑当设置secureTextEntry后, 将textfield重新赋值一次,即额外手动触发器一次text改变:
解决代码如下:
- (void)showPasswordAction {
// fix cursor bug: reset text to refresh cursor
NSString *tempStr = self.textField.text;
self.textField.text = nil;
self.textField.text = tempStr;
[self setTextFiledSecureTextEntry:!self.textField.secureTextEntry];
}
3. text内容显示不全
现象:
光标移到其他textfield上,然后点击show的button,这时候就会出现部分字母显示不全的情况:
可当光标移到这个textfiled上的时候,就会恢复正常
那怎么解决呢?
如果要解决的话,比较简单的方式就是当点击show (就是这个"眼睛")这个button的时候,将光标移到当前textfield上.
解决代码如下:
- (void)showPasswordAction {
// 这种代码一定要加注释,否则后面加入的同学要疯掉了...😢
if (![self.textField isFirstResponder]) {
[self.textField becomeFirstResponder];
}
}
解决后:
4. 字体问题
现象:
password 和new password 设置的是相同的字体,但是会出现2者字体不一样的状态:
字体不对? 于是打了log看,发现本来设置的是系统字体,但是当光标移到password的时候,字体会变成new roman字体.
于是尝试再切换secureTextEntry的时候,重新再设置下字体
//setLcFont 这个是我们自己定义的一个方法,无他尔...
[self.textField setLcFont:[LcFont br17]];
But,
But!!!
Failed!
打了log看,设置后字体是需要的br17字体,但是实际显示的却是错误的字体
后来查了半天,才发现是一个iOS的bug:
可以参考http://stackoverflow.com/questions/35293379
那怎么解决呢?
/* fix font bug:
1.font will change when secureTextEntry changed
2.iOS bug:font setting will not take effect until you set the font to nil
first
*/
[self.textField setFont:nil];
[self.textField setLcFont:[LcFont br17]];
因祸得福
当这个问题解决的时候,第二个光标刷新的问题也顺道解决了,
当然那个问题本质也应该是字体问题,刷新光标的解决方案也只是个trick技巧而已
以上,是昨天遇到的几个小问题
总结下代码就是:
第二天更新:
5. 密码不显示省略号
今天又有新需求:
如下,当密码输入过多的时候,因为显示不下,所以会显示"..."
现在是希望,如果是明文密码,则可以显示"...",但如果是暗文,则不显示"..."
开玩笑,这怎么可能,UITextField不是UILabel,不可以设置line breaks, UILabel 可以设置的type如下:
// NSParagraphStyle
typedef NS_ENUM(NSInteger, NSLineBreakMode) {
NSLineBreakByWordWrapping = 0, // Wrap at word boundaries, default
NSLineBreakByCharWrapping, // Wrap at character boundaries
NSLineBreakByClipping, // Simply clip
NSLineBreakByTruncatingHead, // Truncate at head of line: "...wxyz"
NSLineBreakByTruncatingTail, // Truncate at tail of line: "abcd..."
NSLineBreakByTruncatingMiddle // Truncate middle of line: "ab...yz"
} NS_ENUM_AVAILABLE(10_0, 6_0);
但UITexField没有这个属性,查了半天,也是相关UITexField搞不定的用UITextView,但那是适用于多行显示的问题,与本问题无关.
几经周折("人和人的区别就在这里...")
终于搞定.
那怎么解决呢?
解决方案:
根据textfield的text的内容动态调整期frame,这样就不会出现"..."了 (出现截断的本质是因为width不够,那就增加宽度就好)
当然修改text的frame,这个方法隐藏的比较深了...
- (CGRect)textRectForBounds:(CGRect)bounds {
// Not show truncation text for secureTextEntry,like ****...
if (self.text.length > 0) {
if (self.secureTextEntry && ![self isFirstResponder]) {
CGRect rect = bounds;
CGSize textSize = [self.text sizeOfFont:self.font andWidth:CGFLOAT_MAX];
rect.size.width = MAX(textSize.width, bounds.size.width);
return rect;
}
}
return [super textRectForBounds:bounds];
}
解释下:
- (CGRect)textRectForBounds:(CGRect)bounds:
lets you set the rectangle for the text when the text field is not being edited.
- (CGRect)editingRectForBounds:(CGRect)bounds:
lets you set the rectangle for the text when the text field is being edited.
// 重写绘制行为
# 除了UITextField对象的风格选项,
# 你还可以定制化UITextField对象
# 为他添加许多不同的重写方法,来改变文本字段的显示行为。
# 这些方法都会返回一个CGRect结构
# 制定了文本字段每个部件的边界范围。以下方法都可以重写。
– textRectForBounds: //重写来重置文字区域
– drawTextInRect: //改变绘文字属性.
//重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了.
– placeholderRectForBounds: //重写来重置占位符区域
– drawPlaceholderInRect: //重写改变绘制占位符属性.
//重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了.
– borderRectForBounds: //重写来重置边缘区域
– editingRectForBounds: //重写来重置编辑区域
– clearButtonRectForBounds: //重写来重置clearButton位置,改变size可能导致button的图片失真
– leftViewRectForBounds:
– rightViewRectForBounds:
结果如下:
参照 http://stackoverflow.com/questions/3287312
第三天更新:
6. placeholder 的字体问题
6.1 背景:
大家不知是否记得我们的第四条字体问题,为了修改字体,使用了如下方法:
[self.textField setFont:nil];
[self.textField setLcFont:[LcFont br17]];
这个本来安心的交给测试了,结果产生了新的问题:
6.2 现象:
这个方法会改变placeholder的字体:
默认情况:
当什么都不输入,直接点击眼睛:
(即执行如上setLcFont设置字体的操作之后)
大家可以看到"6-20 characters" 这个字符变小了.
至于为什么会发生这个,很显然是个苹果的bug,
首先通过先设置为nil再设置字体的方式,本身就很诡异,自然会引发出新的问题.
目前表现就是placeholder的字体不对了
6.3 那怎么解决呢
那我们就看下怎么修改placeholder的字体,
6.3.1 attributeString
大概看了下,常见思路就是,设置attributestring,这个看上去可以,但是得把代码中所有的都这么改,而且attribute本身设置起来--"又臭又长",不太实用
所以pass.
6.3.2 placeholderLabel
placeholder 本质是个label,
有个比较常见的设置placeholder color的方法:
[textfield setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
当然这个函数也直接可以通过xcode进行设置完成.
但这个有人反馈自己因为这个被APPSTORE拒绝过.
所以还是pass
6.3.3 自己来渲染
6.3.3.1 textRectForBounds启发
昨天有介绍过textRectForBounds这个方法,那自然对应也有placeholder相关的方法:
/*
Draws the receiver’s placeholder text in the specified rectangle.
You should not call this method directly.
If you want to customize the drawing behavior for the placeholder text,
you can override this method to do your drawing.
By the time this method is called, the current graphics context is already configured with the default environment and text color for drawing.
In your overridden method, you can configure the current context further and then invoke super to do the actual drawing or do the drawing yourself.
If you do render the text yourself, you should not invoke super.
*/
- (void)drawPlaceholderInRect:(CGRect)rect {}
这个如果你完全自己定义placeholder的text,那就不需要调用super方法,否则建议不然忘记super方法.
6.3.3.2 实现drawPlaceholderInRect
show me the code:
- (void)drawPlaceholderInRect:(CGRect)rect {
//draw place holder.
[[self placeholder] drawInRect:rect withFont:[UIFont systemFontOfSize:17]];
}
okey,这样一来,字体就保证了,但是run起来会发现,字体的颜色和位置不对了 ---- 始终处于置顶的感觉
6.3.3.3 解决对齐问题
颜色的问题好解决,我们先来看看置顶的问题:
-
调整垂直对齐方式
如下:
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
发现无效. 然后依次设置了如下全部ContentVerticalAlignment对齐方式后,只有AlignmentBottom会置底,其他都会置顶.
typedef NS_ENUM(NSInteger, UIControlContentVerticalAlignment) {
UIControlContentVerticalAlignmentCenter = 0,
UIControlContentVerticalAlignmentTop = 1,
UIControlContentVerticalAlignmentBottom = 2,
UIControlContentVerticalAlignmentFill = 3,
};
-
drawInRect
前面的不行,那只能从drawInRect下手了,这里断点查了下rect如下:
截图可以看到y = 0,所以导致无法居中,猜想super的
drawPlaceholderInRect方法里面应该是做了一些对齐的操作,那我们也自己来实现一下吧,这个倒是不难
CGSize size = [self.placeholder sizeOfFont:self.font andWidth:CGFLOAT_MAX];
CGRect placeholderRect =
CGRectMake(rect.origin.x, (rect.size.height - size.height) / 2,
rect.size.width, size.height);
参照http://stackoverflow.com/questions/19093604
-
还差一点
在‘2’的基础上测试之后,发现位置是终于居中了,但是点击"eye" buton的时候,位置还是会稍微的有一点点偏移,查了下代码,如果rect会有改变,应该是UITextfied在你编辑状态和非编辑状态的时候,位置会有一定的出入(这个稍微留心点,都能发现)
所以我们的代码还需要再做一丁点的改变:
CGRect placeholderRect =
CGRectMake(rect.origin.x, (self.height - size.height) / 2,
rect.size.width, size.height);
再测试一下,okey.
6.3.3.3 解决颜色问题
这个没什么方法,扣色调整下就行,不过整体扣色还是不太准确,需要自己再去调试.
比较常见的颜色是
[UIColor colorWithWhite:0.70 alpha:1]
不过我试下来,感觉还是不对,自己调整了,大概颜色是:
[UIColor colorWithHexString:@"CDCDC8"] colorWithAlphaComponent:0.8]
不过这个不是重点了,找自己家里的UI去做就好了...